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

只谈情不闲聊 提交于 2020-03-03 22:20:55
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;}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!