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[i][j] 为以 i 为根节点,选出 j 个节点的最大价值(包括 i 这个节点)
转移方程:dp[i][j]=max(dp[i1][j1]+dp[i2][j2]+....+dp[ik][jk])+a[i] j1+j2+...+jk=j-1
由于这个题目上的树并非一棵树而是一个森林,因此我们通过把根设为0使其变成真正意义上的树
那么答案:dp[0][m+1]
看到这个转移方程可能一时难以下手,因为你当前的状态转移是要搞定子节点的各个分支上的情况的
即子节点上各个分支分别取了多少个物品,才能使得决策最优
而对于这个问题,我们可以发现与经典的背包问题存在着相似性,这里的 j 其实就是背包的容量
因此对于这个子问题,我们可以用背包来取最优
记 dp0[i][j] 为以 i 为根节点,它的分支中取出 j 个节点的最大价值
那么转移方程就是经典的背包
dp0[i][j+k]=max{dp0[i][j+k],dp0[i][j]+dp[son[i]][k]}
即我们在一个分支中最优的取出 k 个物品后,从 j 转移到了 j+k
从这里我们也可以看出
本题的DP状态和子问题背包的DP状态其实两个是紧密相连的
两者的区别也仅仅在于本题的DP状态对于以 i 为根节点的子节点情况,它是包括 i 这个根节点本身的
而背包的状态则是不包括在内的,而就是这样的区别导致它们在具体转移的时候方程是很不一样的
code:
View Code
//邻接表#include<stdio.h>#include<string.h>#define max(a,b)((a)>(b))?(a):(b)#define N 205int n,m;struct node{ int from,to,next;}edge[N];int head[N],tot,v[N],ans[N],dp[N][N],f[N][N];void add(int a,int b){ edge[tot].from=a; edge[tot].to=b; edge[tot].next=head[a]; head[a]=tot++;}void dfs(int root){ int j,u,k,i; v[root]=1; for(i=head[root];i!=-1;i=edge[i].next) { u=edge[i].to; if(!v[u]) { dfs(u); for(k=m;k>=0;k--) for(j=0;j<=k;j++) f[root][k]=max(f[root][k],f[root][k-j]+dp[u][j]); } } for(j=1;j<=m+1;j++) dp[root][j]=f[root][j-1]+ans[root];}int main(){ int i,a,b; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0)break; tot=0; memset(head,-1,sizeof(head)); for(i=1;i<=n;i++) { scanf("%d%d",&a,&b); ans[i]=b; add(a,i); } ans[0]=0; memset(v,0,sizeof(v)); memset(dp,0,sizeof(dp)); memset(f,0,sizeof(f)); dfs(0); printf("%d\n",dp[0][m+1]); } return 0;}//邻接矩阵#include<stdio.h>#include<string.h>#define max(a,b)(a)>(b)?(a):(b)int n,m;int wi[201];int v[201];int g[201][201];int f[201][201];int dp[201][201];void dfs(int r){ int tmp,i,j,k; v[r]=1; dp[r][0]=0; f[r][0]=0; for(i=1;i<=n;i++) { if(g[r][i]) { tmp=i; if(!v[tmp]) dfs(tmp); for(j=m;j>=0;j--) { for(k=0;k<=j;k++) { if(f[r][j-k]!=-1&&dp[tmp][k]!=-1) f[r][j]=max(f[r][j],f[r][j-k]+dp[tmp][k]); } } } } for(j=1;j<=m+1;j++) if(f[r][j-1]!=-1) dp[r][j]=f[r][j-1]+wi[r];}int main(){ int i,a,b; while(scanf("%d%d",&n,&m)&&(n&&m)) { memset(f,-1,sizeof(f)); memset(v,0,sizeof(v)); memset(dp,-1,sizeof(dp)); memset(g,0,sizeof(g)); for(i=1;i<=n;i++) { scanf("%d%d",&a,&b); g[a][i]=1; wi[i]=b; } wi[0]=0; dfs(0); printf("%d\n",dp[0][m+1]); } return 0;}//左儿子,有兄弟,多叉转二叉#include<stdio.h>#include<string.h>int wi[201],l[201],r[201],dp[201][201];int v[201][201];int n,m;int max(int x,int y){ return x>y?x:y;}int dfs(int x,int y) { int i; if(x==-1||y==0) return 0; if(dp[x][y]!=-1) return dp[x][y]; int a=dfs(r[x],y); dp[x][y]=a; for(i=0;i<y;i++) { int a2=dfs(l[x],i); int a3=dfs(r[x],y-i-1); dp[x][y]=max(dp[x][y],a2+a3+wi[x]); } return dp[x][y];}int main(){ int i,j,a,b; while(scanf("%d%d",&n,&m)&&(n&&m)) { memset(l,-1,sizeof(l)); memset(r,-1,sizeof(r)); memset(dp,-1,sizeof(dp)); memset(v,0,sizeof(v)); for(i=1;i<=n;i++) { scanf("%d%d",&a,&b); wi[i]=b; if(l[a]!=-1) { int u=l[a]; while(r[u]!=-1) u=r[u]; r[u]=i; } else l[a]=i; } dfs(0,m+1); printf("%d\n",dp[0][m+1]); } return 0;}
来源:https://www.cnblogs.com/dream-wind/archive/2012/03/22/2412021.html