[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的主要内容,如果未能解决你的问题,请参考以下文章

51Nod 1378 夹克老爷的愤怒

51nod1380 夹克老爷的逢三抽一

51nod1625 夹克爷发红包(贪心+dfs)

51nod 1380 夹克老爷的逢三抽一 堆 脑洞题

51Nod 1380 夹克老爷的逢三抽一

51Nod1785 数据流中的算法