HDU1561 The more, The Better

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   |   We have 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号节点没有被访问

   
       

 

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]:因为增加了一个根节点

 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
",dp[0][m]);
60     }
61     return 0;
62 }

没过的代码:

错误:第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 } 
原文地址:https://www.cnblogs.com/Renyi-Fan/p/7430988.html