标签:
卡在为什么是分组背包的问题上,看了下面几句话理解了
题意就是给定n个点,每个地点有value[i]的宝物,而且有的宝物必须是另一个宝物取了才能取,问取m个点可以获得的最多宝物价值。
一个子节点就可以返回m个状态,每个状态表示容量为j(j<=m)时选最多的宝物,而一个子节点中只可以选择一个状态进行转移,每个节点有若干个子节点,问题就转换为分组背包,几个子节点就是几个分组背包,体积是选几个地点,价值是宝物价值。
状态转移方程: dp[v][1] = Money[v]; (v为叶子节点)
dp[v][j] = max(dp[v][j],dp[v][j-i] + dp[k][i] );(v为非叶子节点,j表示用户个数,i为容量,k为v的子节点,)
1 /* 2 HDU 1561 The more, The Better 3 树形DP + 分组背包 4 建立一颗数,选了子节点,必选父亲结点。 5 对于每个结点,它的子结点就是个分组背包 6 */ 7 #include<stdio.h> 8 #include<algorithm> 9 #include<string.h> 10 #include<iostream> 11 using namespace std; 12 const int MAXN=220; 13 struct Node 14 { 15 int to; 16 int next; 17 }; 18 Node edge[MAXN];//建立的有向数,和结点数一样就可以了 19 int tol; 20 int head[MAXN];//头结点 21 int value[MAXN];//每个结点的宝物数量 22 23 int dp[MAXN][MAXN];//dp[i][j]表示在以i为根的子树上,攻克j个结点获得的最大价值 24 25 void init() 26 { 27 memset(head,-1,sizeof(head)); 28 tol=0; 29 memset(dp,0,sizeof(dp)); 30 } 31 32 void add_edge(int a,int b) //建立一条a->b的有向边 33 { 34 edge[tol].to=b; 35 edge[tol].next=head[a]; 36 head[a]=tol++; 37 } 38 39 int n,m; 40 41 void dfs(int u) 42 { 43 dp[u][1]=value[u];//选一个肯定选自己这个结点 44 for(int i=head[u];i!=-1;i=edge[i].next) 45 { 46 int v=edge[i].to; 47 dfs(v); 48 //分组背包,外层是对所有的组(参考背包九讲) 49 for(int k=m;k>=1;k--) //for v <- V to 0 50 //下面这个循环必须是j<k结束,不能取等号,因为至少需要留一个点来取u点 51 for(int j=1;j<k;j++) // do for 所有的属于当前组 52 dp[u][k]=max(dp[u][k],dp[u][k-j]+dp[v][j]); 53 } 54 } 55 56 int main() 57 { 58 int a,b; 59 while(scanf("%d%d",&n,&m)!=EOF) 60 { 61 if(n==0&&m==0)break; 62 init(); 63 for(int i=1;i<=n;i++) 64 { 65 scanf("%d%d",&a,&b); 66 value[i]=b; 67 add_edge(a,i); 68 } 69 value[0]=0; 70 m++;//虚拟构造了结点0 71 dfs(0); 72 73 printf("%d\n",dp[0][m]); 74 } 75 return 0; 76 }
标签:
原文地址:http://www.cnblogs.com/cnblogs321114287/p/4325352.html