洛谷 P2899 [USACO08JAN]手机网络Cell Phone Network
题目描述
Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires him to set up cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (conveniently numbered 1..N) so they can all communicate.
Exactly N-1 pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ A ≤ N; 1 ≤ B ≤ N; A ≠ B) there is a sequence of adjacent pastures such that A is the first pasture in the sequence and B is the last. Farmer John can only place cell phone towers in the pastures, and each tower has enough range to provide service to the pasture it is on and all pastures adjacent to the pasture with the cell tower.
Help him determine the minimum number of towers he must install to provide cell phone service to each pasture.
John想让他的所有牛用上手机以便相互交流(也是醉了。。。),他需要建立几座信号塔在N块草地中。已知与信号塔相邻的草地能收到信号。给你N-1个草地(A,B)的相邻关系,问:最少需要建多少个信号塔能实现所有草地都有信号。
输入输出格式
输入格式:
-
Line 1: A single integer: N
- Lines 2..N: Each line specifies a pair of adjacent pastures with two space-separated integers: A and B
输出格式:
- Line 1: A single integer indicating the minimum number of towers to install
样例输入:
5 1 3 5 2 4 3 3 5
样例输出:
2
解析:当然是树形dp了;本题很好的参照是一道叫做 皇宫看守 的树形dp(在小白菜oj上可以看到,还有题解和视频解析)
解释全在代码里:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define man 10050 5 int n,f[man][4]; 6 //这里的安全表示以x为根的子树全部都安全 7 //这里的不安全表示以x为根的子树除了x都安全 8 //f[][0]不放塔但安全 9 //f[][1]不放塔并且不安全 10 //f[][2]放塔但安全 11 //f[][3]放塔还是不安全(肯定不存在,所以不用考虑) 12 struct edge{int next,to;}e[man<<2]; 13 int head[man<<2],num=0; 14 inline void add(int from,int to) 15 {e[++num].next=head[from];e[num].to=to;head[from]=num;} 16 void treedp(int x,int fa) 17 { f[x][0]=f[x][1]=0;f[x][2]=1;int minn=12456789;//当f[][2]时,初始值肯定有他自身的价值 18 bool changed=0;//记录是否要更改f[x][0]的值 19 for(int i=head[x];i;i=e[i].next) 20 { int to=e[i].to; 21 if(to==fa)continue;//防止重新搜回去 22 treedp(to,x); 23 minn=min(minn,f[to][2]-f[to][0]);//预处理如果将一个点从不放塔变为放塔,那么他的最小代价是多少 24 f[x][0]+=min(f[to][0],f[to][2]);//先把最小的价值加上去,如果不行的话下面再改; 25 if(f[to][2]<=f[to][0]) changed=1;//如果放了塔的价值比不放塔的价值还小,那么肯定放塔咯,因为放塔了还可以多照看几个地方(并且还可以把他的父亲一起看了) 26 f[x][1]+=f[to][0];//本身的f[][1]就是表示不放塔并不安全,那么只能叫它这么继续错下去,因为是当前这个点变得安全是f[][0]和f[][2]的责任,不需要f[][1]的干涉 27 f[x][2]+=min(f[to][0],min(f[to][1],f[to][2]));//因为当前放了塔并且安全,那么他的子树放不放塔肯定都安全,所以子节点的任何状态都可以成立 28 } 29 if(changed==0) f[x][0]+=minn;//如果他的子树都不放塔,那么当前的节点就不安全了,所以必须把他的子节点中放塔的代价最小的点放塔,这样才能保证他的安全 30 } 31 int main() 32 { scanf("%d",&n); 33 for(int i=1;i<n;i++) 34 { int u,v; 35 scanf("%d%d",&u,&v); 36 add(u,v);add(v,u); 37 } 38 treedp(1,-1); 39 printf("%d\n",min(f[1][0],f[1][2])); 40 return 0; 41 }