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 }

 

转载于:https://www.cnblogs.com/luomi/p/5590145.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值