本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

HDOJ (HDU) 1561 The more, The Better (树形DP)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1561

很明显这是个分组的有依赖的背包问题(背包问题的扩展请参考 Tianyi Cui 的背包九讲 http://love-oriented.com/pack/ )。

构建树:若攻击城堡 a 要先攻击城堡 b,则 a 是 b 的儿子,这样构建以后是一个森林,添加一个宝物数量为0的节点作为整个森林的树根以后就是一颗树了。

递推方程:设 dp[a][b] 表示以 a 为根节点的子树,要攻击 b 个城堡所获得的最大金钱数目。其中 a 城堡是已经攻下来的。

               则 dp[a][b] = max( dp[a][b],  dp[a][b - k] + dp[ son(a) ][k] )  ,其中son(a) 表示 a 的儿子节点。

               递推方程的意思是:在 a 的子节点子树中选k个点加上a 子树选 b-k 个点 与直接在 a 子树选b个点谁更优。

              (其实背包问题或者类背包问题都有相似的递推方程)

初始化:dp[][] 开始均初始化为0,且dp[a][1] 均初始化为节点 a 的宝物数量。

注意细节:算法的第二层循环(代码24行)为什么b要从大到小:dp[root][b] 是由dp[root][b - k] 和 dp[tree[root][i]][k]决定,表面上看b应该从小到大循环,但是从最外层循环(代码21行)来看,

每遍历完一个儿子,dp[root][]都被计算了一遍,在遍历下一儿子时,dp[root][b] 是由遍历上次儿子时已经计算好的dp[root][b - k] 和 dp[tree[root][i]][k]决定,若从小到大循环,则上次计算好的

dp[root][b - k]就被覆盖了。

废话少说,上代码:

 1 #include<iostream>
 2 #include<string>
 3 #include<vector>
 4 using namespace std;
 5 
 6 const int SIZE = 201;
 7 int max(int a, int b)
 8 {
 9     return a>b ? a:b; 
10 }
11 //////////////////////////////////////////////////////////////////////    
12 vector<int> tree[SIZE];  //tree[i][j]表示节点i的第j个儿子的id
13 bool visit[SIZE];        //树节点是都访问过
14 int dp[SIZE][SIZE];
15 int N,M;
16 
17 void dfs_dp(int root) //tree dp
18 {
19     if(visit[root])return;
20     visit[root] = true;
21     for(int i = 0; i < tree[root].size(); i++)//遍历所有儿子
22     {
23         if(!visit[tree[root][i]])dfs_dp(tree[root][i]); //深度优先
24         for(int b = M; b > 1; b--) //循环变量 b 一定是从大到小!!!!!!!!!!
25             for(int k = 1; k < b; k++)
26             {
27                 dp[root][b] = max(dp[root][b], dp[root][b - k] + dp[tree[root][i]][k]);
28             }
29     }
30 }
31 
32 //将id=0的节点视为整个树的根,其宝物数量为 0;        
33 int main()
34 {
35     while((cin>>N>>M) && (N || M))
36     {
37         //init
38         for(int i = 0; i < SIZE; i++)
39             tree[i].clear();
40         memset(visit, 0, sizeof(visit)); 
41         memset(dp, 0, sizeof(dp));  
42         
43         int father,val;
44         for(int i = 1; i <= N; i++)
45         {
46             cin>>father>>val;
47             tree[father].push_back(i);
48             dp[i][1] = val;
49         }
50         
51         M++; //由于添加了0号节点为树根,所以,相当于多加了个城堡,且这个城堡必须攻克
52         dfs_dp(0);
53         cout<<dp[0][M]<<endl;
54     }
55     
56     return 1;
57 }
View Code

 【版权声明】转载请注明出处 http://www.cnblogs.com/TenosDoIt/archive/2013/06/15/3137679.html

 

posted @ 2013-06-15 15:45  tenos  阅读(722)  评论(0编辑  收藏  举报

本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

公益页面-寻找遗失儿童