[51nod] 1378 夹克老爷的愤怒 #树形DP
Posted Lev今天学习了吗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[51nod] 1378 夹克老爷的愤怒 #树形DP相关的知识,希望对你有一定的参考价值。
1378 夹克老爷的愤怒
基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
夹克老爷逢三抽一之后,由于采用了新师爷的策略,乡民们叫苦不堪,开始组织起来暴力抗租。
夹克老爷很愤怒,他决定派家丁常驻村中进行镇压。
诺德县 有N个村庄,编号0 至 N-1,这些村庄之间用N - 1条道路连接起来。
家丁都是经过系统训练的暴力机器,每名家丁可以被派驻在一个村庄,并镇压当前村庄以及距离该村庄不超过K段道路的村庄。
夹克老爷一贯奉行最小成本最大利润的原则,请问要实现对全部村庄的武力控制,夹克老爷需要派出最少多少名家丁?
Input
第1行:2个数N, K中间用空格分隔(1<= N <= 100000, 0 <= K <= N)。 之后N-1行:每行2个数S, E中间用空格分隔,表示编号为S的村庄同编号为E的村庄之间有道路相连。(0 <= S, E < N)。
Output
输出一个数说明要实现对全部村庄的武力控制,夹克老爷需要派出最少多少名家丁?
Input示例
4 1 0 1 0 2 0 3
Output示例
1
Analysis分析
Emmmm 显然树形DP
那么我们记录状态为 DP[ u ][ 1/0 ],u为当前点,DP[ u ][ 1 ] 表示完全覆盖 u 以及 u 的子树,DP[ u ][ 0 ] 表示完美覆盖 u 以及 u 的子树
完美覆盖是指:所有的家丁的管辖范围都没有重叠,最大效率的利用家丁进行镇压,哪怕是没有覆盖所有点也不能有所重叠
完全覆盖是指所有点都被覆盖,哪怕出现重叠
其实唯一可能有的区别就是 u 这个点有没有家丁(= =当然有的时候这两个状态是一样的:如果当前点必须放置家丁的话)
现在来确定怎么放最好
对于一条链,显然在离端点距离为 k 的那个点放下去是最优的
那么对于一个树上的点,如果其子树未控制的点的深度和他相差 ≥k 的话,这个点必须要放置家丁:他的父节点是没有办法控制那个最深的未控制点的
这是必须的情况,那么还有不必须的情况:该节点其中一个儿子支系中有家丁可以控制很远的距离,以至于可以通过 u 控制到其他子节点
这种情况就需要计算了:如果那个家丁可以控制到所有子节点中最深的未控制点,那么当前这个点就没必要设家丁了
但是如果不行, u 必须设置。具体的判别式见代码
我相信我的代码可读性非常好因此我就不画图了(其实是时间紧懒得画 笑)
那么我们对于每个点就需要设置四个参数:完美覆盖最小代价,完全覆盖最小代价,u及u的子树中所能控制到的最浅的深度(buoy),u及u的子树中未控制的最深的深度(anch)
然后就嘿嘿嘿嘿
Code代码
1 #include<stdio.h> 2 #include<iostream> 3 #define maxn 1000000 4 using namespace std; 5 6 int DP[maxn][5],buoy[maxn],anch[maxn],depth[maxn],sz[maxn],n,k; 7 8 struct edge{ 9 int from,v; 10 }e[maxn*2]; int tot,first[maxn]; 11 void insert(int u,int v){ tot++; e[tot].from = first[u]; e[tot].v = v; first[u] = tot; } 12 13 void dfs(int u,int pre){ 14 int sum = 0; sz[u] = 1; 15 DP[u][0] = DP[u][1] = 0; buoy[u] = 1e9; anch[u] = -1e9; 16 for(int i = first[u];i;i = e[i].from){ 17 int v = e[i].v; if(v == pre) continue; 18 depth[v] = depth[u]+1; 19 dfs(v,u); 20 sz[u] += sz[v]; 21 buoy[u] = min(buoy[u],buoy[v]); 22 anch[u] = max(anch[u],anch[v]); 23 sum += DP[v][0]; 24 } 25 26 if(sz[u] == 1){ 27 DP[u][1] = 1; 28 DP[u][0] = 0; 29 if(!k) DP[u][0] = 1; 30 buoy[u] = anch[u] = depth[u]; 31 return; 32 } 33 34 // printf("#%d: buoy%d anch%d\n",u,buoy[u],anch[u]); 35 if(anch[u]-depth[u] >= k){ // Must set 36 // printf("#%d: Poi A\n",u); 37 DP[u][0] = DP[u][1] = sum+1; 38 buoy[u] = depth[u]-k; 39 anch[u] = buoy[u]-1; 40 }else if(2*depth[u]-buoy[u] < anch[u]){ // Can choose 41 // printf("#%d: Poi B\n",u); 42 DP[u][0] = sum; 43 DP[u][1] = sum+1; 44 }else{ // Needn‘t 45 // printf("#%d: Poi C\n",u); 46 DP[u][0] = DP[u][1] = sum; 47 anch[u] = buoy[u]-1; 48 } 49 } 50 51 int main(){ 52 scanf("%d%d",&n,&k); 53 54 for(int i = 1;i < n;i++){ 55 int u,v; scanf("%d%d",&u,&v); 56 u++,v++; insert(u,v); insert(v,u); 57 }depth[1] = 1; 58 59 dfs(1,1); 60 61 printf("%d\n",DP[1][1]); 62 63 // for(int i = 1;i <= n;i++){ 64 // printf("#%d: 0:%d 1:%d buoy:%d anch:%d\n",i,DP[i][0],DP[i][1],buoy[i],anch[i]); 65 // } 66 67 return 0; 68 }
以上是关于[51nod] 1378 夹克老爷的愤怒 #树形DP的主要内容,如果未能解决你的问题,请参考以下文章