hdu1011_树形dp
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011
(转)说实话,第一次做树形dp这类题目,答案参考网上做的,有点头疼,应该做多了就好了吧,凡事都是熟能生巧!
有 n(1<n<=100) 个山洞,每个山洞中都有一些 bug,每个山洞中都有一定的概率包含一个 brain。所有的山洞形成一棵树,现在给你 m(0<=m<=100) 个士兵,每个士兵都能消灭 20 个 bugs,并占领这个山洞,山洞的入口的编号是 1,问怎么安排士兵占领山洞才能使捕获 brain 的概率最大!
典型的树上背包问题
定义状态 f[u][P] 表示用 P 个士兵占领以 u 为根节点的子树所能获得的概率最大值,状态转移就是一个树形DP过程,目标状态就是 f[1][m]。
f[u][p] = max {f[u][p], f[u][p - k] + f[v][k] };其中v是u的子节点。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cstdlib> 6 #include <cmath> 7 #include <set> 8 #include <map> 9 #include <vector> 10 #define N 105 11 using namespace std; 12 13 vector <int> v[N];//图 14 int n, m, bug[N], brain[N], dp[N][N];/*dp[u][p]表示用 P 个士兵占领以 u 为根节点的子树所能获得的概率最大值*/ 15 void dfsdp(int p, int pre) 16 { 17 for(int i = bug[p]; i <= m; i++)/*初始化,首先将dp[p][i]里面填充进brain[p],后面可以更新dp[p][i]的值*/ 18 dp[p][i] = brain[p];/*也就是说当我们有bug[p]名队员以至于更多时,我们最少可以获得brain[p]个大脑*/ 19 int size = v[p].size(); /*num指p节点含有的支路数*/ 20 for(int i = 0; i < size; i++) /*一条支路一条支路遍历,也就是所谓的dfs*/ 21 { 22 int temp = v[p][i]; 23 if(temp == pre) /*避免死循环,节点如果是根部,就继续*/ 24 continue; 25 dfsdp(temp, p);/*递归解决问题,先将子节点的所能得到的最大值计算出来*/ 26 for(int j = m; j >= bug[p]; j--)/*当队员人数一定时*/ 27 for(int k = 1; k <= j - bug[p]; k++)/*由于p节点一定要通过,所以一定要花费bug[p]*/ 28 dp[p][j] = max(dp[p][j - k] + dp[temp][k], dp[p][j]); 29 /*v节点就两种状态,要么选择,要么不选择,选择的话dp[p][j] = dp[p][j - k] + dp[v][k],不选择的话就不变*/ 30 } 31 } 32 int main() 33 { 34 int x, y; 35 while(~scanf("%d %d", &n, &m) && (n != -1 && m != -1)) 36 { 37 for(int i = 1; i <= n; i++) 38 { 39 v[i].clear(); 40 scanf("%d %d", &x, &brain[i]); 41 bug[i] = (x + 19) / 20; 42 } 43 for(int i = 1; i < n; i++) 44 { 45 scanf("%d %d", &x, &y); 46 v[x].push_back(y); 47 v[y].push_back(x); 48 } 49 if(m == 0){ 50 printf("0\n"); 51 continue; 52 } 53 memset(dp, 0, sizeof(dp)); 54 dfsdp(1, 0); 55 printf("%d\n", dp[1][m]); 56 } 57 return 0; 58 }