HDU - 1561 - The more, The Better

先上题目:

The more, The Better

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4701    Accepted Submission(s): 2777


Problem Description
ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?
 

 

Input
每个测试实例首先包括2个整数,N,M.(1 <= M <= N <= 200);在接下来的N行里,每行包括2个整数,a,b. 在第 i 行,a 代表要攻克第 i 个城堡必须先攻克第 a 个城堡,如果 a = 0 则代表可以直接攻克第 i 个城堡。b 代表第 i 个城堡的宝物数量, b >= 0。当N = 0, M = 0输入结束。
 

 

Output
对于每个测试实例,输出一个整数,代表ACboy攻克M个城堡所获得的最多宝物的数量。
 

 

Sample Input
3 2
0 1
0 2
0 3
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
0 0
 

 

Sample Output
5
13
  中文题意不解释,这题目可以看成是一个森林,对于需要攻占的城堡作为根节点,攻占了这个根节点以后可以立即攻占的城堡作为它的孩子,那么这就变成了树形DP了,因为这有可能是一个森林,所以我们将一开始就可以攻占的城堡作为0号节点的孩子,这样就可以将一个森林转化成一棵树了,然后对于每一个节点,因为它的孩子的组合有多种,同时孩子的孩子也会影响它们,所以这个问题可以转化为有依附的背包问题,每一个节点作为原件,它的孩子们作为附件,然后对孩子们跑一遍01背包,这样就可以用孩子的属性求出整棵子树的属性,然后一层一层地想向上推,最终就可以得到结果了。
  状态转移方程:dp[r][j]=max{dp[r][j],dp[r][j-k]+dp[ver[r][i]][k]} 其中r代表以r为根节点的子树;j为容量为j的背包,这里的意思是以r为根节点的子树一共取j个节点;k的意思是从r的第i个孩子的子树那里挑k个节点,ver[r][i]代表r的第i个孩子的标号;dp[r][j]的意思是从以r为根的子树取j个节点的最大价值是多少。
  代码那里有一个地方需要注意的,就是k不可以等于j,因为j是从一棵子树取的节点数,只要j>0,那么j里面一定要包含根节点,k==j的意思就是在不取根节点的情况下在子树上取j个节点,这不合题意。
 
上代码:
 
 1 #include <cstdio>
 2 #include <cstring>
 3 #define max(x,y) (x >= y ? x : y)
 4 #define MAX 202
 5 using namespace std;
 6 
 7 int ver[MAX][MAX];
 8 int dp[MAX][MAX];
 9 int count[MAX];
10 int v[MAX];
11 int n,m;
12 
13 void reset(){
14     memset(ver,0,sizeof(ver));
15     memset(dp,0,sizeof(dp));
16     memset(count,0,sizeof(count));
17     memset(v,0,sizeof(v));
18 }
19 
20 void dfs(int r){
21     for(int i=0;i<count[r];i++){
22         dfs(ver[r][i]);
23     }
24     dp[r][1]=v[r];
25     for(int i=0;i<count[r];i++){
26         int t=ver[r][i];
27         for(int j=m;j>1;j--){
28             for(int k=1;k<j;k++){
29                 /** k不能等于j,因为k==j的意思就是以r为子树的树上取除去根节点以外的节点一共j个,但是因为如果不去r的话,就无法取它的孩子,所以k!=j **/
30                 dp[r][j]=max(dp[r][j],dp[r][j-k]+dp[t][k]);
31             }
32         }
33     }
34 }
35 
36 int main()
37 {
38     //freopen("data.txt","r",stdin);
39     while(scanf("%d %d",&n,&m),(n+m)){
40         reset();
41         for(int i=1;i<=n;i++){
42             int a;
43             scanf("%d",&a);
44             scanf("%d",&v[i]);
45             ver[a][count[a]]=i;
46             count[a]++;
47         }
48         m++;
49         dfs(0);
50         printf("%d\n",dp[0][m]);
51     }
52     return 0;
53 }
1561

 

 
posted @ 2014-03-06 22:24  海拉鲁的林克  阅读(555)  评论(0编辑  收藏  举报