HDU 1561 The more, The Better【树形DP】

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 205
int 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;
}

posted @ 2012-03-22 17:44  'wind  阅读(306)  评论(0编辑  收藏  举报