先上题目:
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 ",dp[0][m]); 51 } 52 return 0; 53 }