[HNOI2003]消防局的设立 树形dp // 贪心
https://www.luogu.org/problemnew/show/P2279
一开始就想到了贪心的方法,不过一直觉得不能证明。
贪心的考虑是在深度从深到浅遍历每个结点的过程中,对于每个没有覆盖的结点选择覆盖他的祖父结点。
仔细想想觉得这是正确的。
在实现的过程中有一个小技巧是o[i]记录i结点距离消防局最近的距离,如果o[i] > 2则需要在他的祖父结点建立一个消防站。用这种方法可以很方便的判断兄弟节点是否被覆盖。
一个细节是要给根节点1建立两个虚结点N + 1和N + 2作为他的父亲结点和祖父结点,不然在1结点寻找祖父节点的时候容易出现问题。
用这样的方法也可以在常数很小的时间内实现树上半径k的覆盖问题
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
inline int read(){int now=;register char c=getchar();for(;!isdigit(c);c=getchar());
for(;isdigit(c);now=now*+c-'',c=getchar());return now;}
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn =;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
int fa[maxn];
int deep[maxn];
int a[maxn];
int o[maxn];
bool cmp(int a,int b){
return deep[a] > deep[b];
}
int main(){
Sca(N); o[] = INF;
fa[] = N + ;
fa[N + ] = N + ;
o[N + ] = o[N + ] = INF;
for(int i = ; i <= N; i ++) a[i] = i;
for(int i = ; i <= N ; i ++){
Sca(fa[i]);
deep[i] = deep[fa[i]] + ;
o[i] = INF;
}
sort(a + ,a + + N,cmp);
int ans = ;
for(int i = ; i <= N ; i ++){
int u = a[i];
int v = fa[u],w = fa[fa[u]];
o[u] = min(o[u],min(o[v] + ,o[w] + ));
if(o[u] > ){
o[w] = ;
o[fa[w]] = min(o[fa[u]],);
o[fa[fa[w]]] = min(o[fa[fa[w]]],);
ans++;
}
}
Pri(ans);
return ;
}
贪心
当然这道题出现在dp专题里,事实上也是一个很硬核的树dp,在没有想到或者证明贪心的时候,这样明显的树dp也不失为一种做法。
思路借鉴了luogu的题解,开始对于这样的树dp一直想着两次树dp,第一次记录根节点的answer,第二次利用根节点拓展到整棵树的answer,导致开始的思路仅限于dp[maxn][3]来记录这个点,这个点的儿子,这个点的孙子的消防站情况,使得第一次dfs就遇到了问题,可能可以做,但有些麻烦。
看了题解之后茅塞顿开,事实上树dp并不需要套路的总是两边dp,换一种思路同时维护两端的情况,就是除了原本的3个状态以外,重新记录dp[i][3]表示所有儿子都被覆盖的情况以及dp[i][4]所有孙子都被覆盖的情况。
仅用一遍dfs就可以解决这个问题。
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include<algorithm>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <functional>
using namespace std;
inline int read(){int now=;register char c=getchar();for(;!isdigit(c);c=getchar());
for(;isdigit(c);now=now*+c-'',c=getchar());return now;}
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
struct Edge{
int to,next;
}edge[maxn * ];
int head[maxn],tot;
void init(){
for(int i = ; i <= N ; i ++) head[i] = -;
tot = ;
}
void add(int u,int v){
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
int dp[maxn][]; // 0这是,1儿子是,2孙子是,3儿子全被覆盖,4孙子全被覆盖
int Min(int a,int b){
return a > b?b:a;
}
int Min(int a,int b,int c){
return Min(Min(a,b),c);
}
int Min(int a,int b,int c,int d){
return Min(Min(a,b,c),d);
}
int Min(int a,int b,int c,int d,int e){
return Min(Min(a,b,c),Min(d,e));
}
void dfs(int t){
if(head[t] == -){
dp[t][] = ;
dp[t][] = dp[t][] = INF;
dp[t][] = dp[t][] = ;
return;
}
dp[t][] = ;
int MAXSON = INF;
int MAXGS = INF;
for(int i = head[t]; ~i; i = edge[i].next){
int v = edge[i].to;
dfs(v);
dp[t][] += Min(dp[v][],dp[v][],dp[v][],dp[v][],dp[v][]);
dp[t][] += Min(dp[v][],dp[v][],dp[v][],dp[v][]);
MAXSON = Min(dp[v][] - Min(dp[v][],dp[v][],dp[v][],dp[v][]),MAXSON);
MAXGS = Min(dp[v][] - Min(dp[v][],dp[v][],dp[v][]),MAXGS);
dp[t][] += Min(dp[v][],dp[v][],dp[v][]);
dp[t][] += Min(dp[v][],dp[v][],dp[v][]);
dp[t][] += Min(dp[v][],dp[v][],dp[v][],dp[v][]);
}
dp[t][] += MAXSON;
dp[t][] += MAXGS;
}
int main(){
Sca(N); init();
for(int i = ; i <= N; i ++){
int x; Sca(x);
add(x,i);
}
dfs();
cout << Min(dp[][],dp[][],dp[][]);
return ;
}
[HNOI2003]消防局的设立 树形dp // 贪心的更多相关文章
- 【BZOJ1217】[HNOI2003]消防局的设立 树形DP
[BZOJ1217][HNOI2003]消防局的设立 Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地, ...
- P2279 [HNOI2003]消防局的设立[树形dp]
题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状 ...
- bzoj1217: [HNOI2003]消防局的设立 [树形dp]
Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了 ...
- luogu 2279 [HNOI2003]消防局的设立 树形dp
就是细节多一些,思路都非常常规. Code: #include <bits/stdc++.h> #define N 1005 #define inf 1061109567 #define ...
- [HNOI2003] 消防局的设立 - 树形dp
仍然是点覆盖集问题,但覆盖半径变成了\(2\) 延续上一题的思路,只是式子更加复杂了 想体验一下min_element大法于是不想优化了 #include <bits/stdc++.h> ...
- [HNOI2003]消防局的设立 (贪心)
[HNOI2003]消防局的设立 题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达, ...
- BZOJ 1217: [HNOI2003]消防局的设立( 贪心 )
一个简单的贪心, 我们只要考虑2个消防局设立的距离为5时是最好的, 因为利用最充分. 就dfs一遍, 再对根处理一下就可以了. 这道题应该是SGU某道题的简化版...这道题距离只有2, 树型dp应该也 ...
- [luogu]P2279 [HNOI2003]消防局的设立[贪心]
[luogu]P2279 [HNOI2003]消防局的设立 题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两 ...
- BZOJ1217: [HNOI2003]消防局的设立
BZOJ1217: [HNOI2003]消防局的设立 Description 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地. 起初为了节约材料,人类只修建了n-1条道路来连接这些基地 ...
随机推荐
- A Simple Problem with Integers(线段树区间更新模板)
最基本的线段树的区间更新及查询和 用tag(lazy)数组来“延缓”更新,查询或添加操作必须进行pushdown操作,即把tag从p传到lp和rp并清楚tag[p],既然得往lp和rp递归,那么就可以 ...
- Android与H5交互 原理与对比
原文: https://www.jianshu.com/p/345f4d8a5cfa 1.Android调用JS的方法有2种: (1)通过WebView的loadUrl() // 调用js中的函数: ...
- Qt setStyleSheet
Qt中设置按钮或QWidget的外观是,可以使用QT Style Sheets来进行设置,非常方便.可以用setStyleSheet("font: bold; font-size:20px; ...
- 傻瓜式搭建私人网络硬盘——owncloud安装指南
百度云这个贱货天天删我资源,我已经忍无可忍了,于是想搭建一个owncloud来放我的里番.使用owncloud不仅安全,而且还可以在线播放,离线下载,功能相当强大. 然而·····网上查了一下,竟然无 ...
- BZOJ1004[HNOI2008]Cards——polya定理+背包
题目描述 小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色 ...
- HDU1075 字典树板子题
题意 :给出两组字符串 一一映射,给出一种组成的文字,要求映射成另外一种思路:使用字典树,把映射的另外一个字符存在字典树的单词节点处 例如 abc 123 则把123存在abc节点中的c处即可 ...
- 「CodeForces - 50C 」Happy Farm 5 (几何)
BUPT 2017 summer training (16) #2B 题意 有一些二维直角坐标系上的整数坐标的点,找出严格包含这些点的只能八个方向走出来步数最少的路径,输出最少步数. 题解 这题要求严 ...
- 自学华为IoT物联网_12 Huawei LiteOS基础架构
点击返回自学华为IoT物流网 自学华为IoT物联网_12 Huawei LiteOS基础架构 一.1个Huawei LiteOS Kernel 1.1 huawei LiteOS Kernel基本框架 ...
- ftp:linux下利用shell脚本添加虚拟用户并赋予权限
首先ftp配置应为虚拟用户登录模式 用户密码文本目录为/etc/vsftpd/vftpuser,代码如下: #!/bin/bash # ];then username=$ password=$ hom ...
- 【BZOJ3691】游行(网络流)
[BZOJ3691]游行(网络流) 题面 BZOJ 然而权限题. Description 每年春季,在某岛屿上都会举行游行活动. 在这个岛屿上有N个城市,M条连接着城市的有向道路. 你要安排英雄们的巡 ...