题目大意
有 n(1<n<=100) 个山洞,每个山洞中都有一些 bug,每个山洞中都有一定的概率包含一个 brain。所有的山洞形成一棵树
现在给你 m(0<=m<=100) 个士兵,每个士兵都能消灭 20 个 bugs,并占领这个山洞
山洞的入口的编号是 1
问怎么安排士兵占领山洞才能使捕获 brain 的概率最大!
做法分析
典型的树上背包问题
定义状态 f[u][P] 表示用 P 个士兵占领以 u 为根节点的子树所能获得的概率最大值
状态转移就是一个树形DP过程
目标状态就是 f[1][m]
一个小trick:即使某个山洞的 bug 数量为 0,那么也至少需要 1 个士兵去获取 brain,所以当现在手上的士兵数量为 0 的时候,可以直接输出 0 了
参考代码
PS:我把所有点的编号都 -1 了
HDU 1011
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 6 using namespace std; 7 8 const int N=101; 9 10 int n, m; 11 int bug[N], p[N], f[N][N]; 12 vector<int> adj[N]; 13 14 void DFS(int u, int pre) 15 { 16 for(int i=bug[u]; i<=m; i++) f[u][i]=p[u]; 17 int tot=(int)adj[u].size(); 18 for(int i=0; i<tot; i++) 19 { 20 int v=adj[u][i]; 21 if(v==pre) continue; 22 DFS(v, u); 23 for(int j=m; j>=bug[u]; j--) 24 for(int k=1; k<=j-bug[u]; k++) 25 if(f[u][j]<f[u][j-k]+f[v][k]) f[u][j]=f[u][j-k]+f[v][k]; 26 } 27 } 28 29 int main() 30 { 31 while(scanf("%d%d", &n, &m), n!=-1 || m!=-1) 32 { 33 for(int i=0; i<n; i++) 34 { 35 scanf("%d%d", &bug[i], &p[i]); 36 bug[i]=(bug[i]+19)/20; 37 } 38 for(int i=0; i<n; i++) adj[i].clear(); 39 for(int i=0, a, b; i<n-1; i++) 40 { 41 scanf("%d%d", &a, &b); 42 adj[a-1].push_back(b-1); 43 adj[b-1].push_back(a-1); 44 } 45 if(m==0) 46 { 47 printf("0\n"); 48 continue; 49 } 50 memset(f, 0, sizeof f); 51 DFS(0, -1); 52 printf("%d\n", f[0][m]); 53 } 54 return 0; 55 }