HDU1561 The more, The Better

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

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

 

 

Author

8600

 

 

Source

​HDU 2006-12 Programming Contest ​

 

 

Recommend

LL   |   Wehave carefully selected several similar problems for you:  ​ ​1011​​ ​​2159​​ ​​2639​​ ​​1203​​ ​​2602​​ 

 

分析:

典型的树形dp的题目。

首先,限制条件是选择m个物品,而每个物品最多选一次,跟0-1背包的区别在于有依赖关系,那么这层依赖关系我们可以借助于一个树来解决。借助dfs,从根节点开始dfs,然后直到叶子节点,回朔的时候进行0-1背包dp。


dp[i][j]表示以i为根节点取j个节点(包括根)的最优值



map[i][j]表示i号节点的第j个孩子是什么


 

 


num[i]表示i号节点的孩子数


 

 

 


vis[i]==0表示i号节点没有被访问


 

 

 

 

 

 

 HDU1561 The more, The Better_i++

dp[i][j]=max(dp[i][j],dp[i][k]+dp[i的某个孩子节点][j-k])

表示在父亲节点i中选k个点和在i的某个孩子中选j-k个点

我们可以知道dp[i][1]=val[i],因为选一个点的话必须选自己 = =

依赖关系形成森林,要先把树转换成森林才能进行树形DP

增加一个根节点0即可

答案即为dp[0][m+1]:因为增加了一个根节点

 

HDU1561 The more, The Better_树形DP_02

 

 



1 #include<stdio.h>
2 #include<string.h>
3 int n,m;
4 int num[250];
5 int map[250][250];
6 int dp[250][250];
7 bool vis[250];
8 int max(int a,int b){
9 return a>b?a:b;
10 }
11 void dfs(int p)
12 {
13 int i,j,k;
14 //将p的访问置为true
15 vis[p]=true;
16 //遍历p的所有孩子
17 for(i=1;i<=num[p];i++)
18 {
19 //t是节点p的第i个孩子
20 int t=map[p][i];
21 //如果t没被访问,dfs它
22 if(!vis[t]) dfs(t);
23 //m反向过来是保证后面的数据不影响前面的,比如当m=5时,j=m,k=2时,等式右边出现过一次dp[p][2]
24 //而当m=5时,j=2,k=2时, 状态转移方程等式左边出现了dp[p][2],显然,这个出现的dp[p][2]不能影响右边那个dp[p][2]
25 for(j=m;j>=2;j--)//选择1个的状态不用更新了,就是节点本身,初始化中已经做了
26 {
27 for(k=1;k<j;k++)//k表示父亲需要取的点的个数,j-k表示孩子需要取的点的个数
28 {
29 //如果有值
30 if(dp[t][j-k]!=-1&&dp[p][k]!=-1)
31 dp[p][j]=max(dp[p][j],dp[p][k]+dp[t][j-k]);
32 }
33 }
34 }
35 }
36 int main()
37 {
38 int i,j;
39 while(scanf("%d%d",&n,&m),n||m)
40 {
41 int a,b;
42 dp[0][1]=0;
43 memset(num,0,sizeof(num));
44 for(i=1;i<=n;i++){
45 scanf("%d%d",&a,&b);
46 //初始化
47 dp[i][1]=b;
48 map[a][++num[a]]=i;
49 }
50 m++;//增加一个点,森林转换成树
51 //初始化
52 for(i=0;i<=n;i++){
53 dp[i][0]=0;vis[i]=0;
54 for(j=2;j<=m;j++){
55 dp[i][j]=-1;
56 }
57 }
58 dfs(0);
59 printf("%d\n",dp[0][m]);
60 }
61 return 0;
62 }


 

 

没过的代码:

HDU1561 The more, The Better_树形DP_03

错误:第30行的g[a][++num[a]]=i;这里写成了g[a][++num[i]]=i; 



1 #include <bits/stdc++.h>
2 const int N=2e2+10;
3 using namespace std;
4 int dp[N][N],m,n;
5 int num[N],g[N][N];
6 bool vis[N];
7
8 void dfs(int r){
9 vis[r]=true;
10 for(int i=1;i<=num[r];i++){
11 int v=g[r][i];
12 if(!vis[v]) dfs(v);
13 for(int j=m;j>=2;j--){
14 for(int k=1;k<j;k++){
15 //这里可以判断一下如果有值的话
16 if(dp[r][k]!=-1&&dp[v][j-k]!=-1)
17 dp[r][j]=max(dp[r][j],dp[r][k]+dp[v][j-k]);
18 }
19 }
20 }
21 }
22
23 int main(){
24 freopen("in.txt","r",stdin);
25 memset(dp,-1,sizeof(dp));
26 while(scanf("%d %d",&n,&m),n||m){
27 for(int i=1;i<=n;i++){
28 int a,w;
29 cin>>a>>w;
30 g[a][++num[a]]=i;//存孩子 //这里写成了g[a][++num[i]]=i;
31 dp[i][1]=w;
32 dp[i][0]=0;
33 }
34 dp[0][1]=0;
35 dp[0][0]=0;
36 m++;
37 dfs(0);
38 cout<<dp[0][m]<<endl;
39 }
40
41
42 return 0;
43 }