本文主要是介绍HDU1561 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
解题报告
我们知道普通的01背包的转移方程:
for(int i=1;i<=n;i++)
for(int j=m;j>=c[i];j++)
f[j]=max(f[j-c[i]]+w[i]);
上述方程f[j]意味着用j的空间来存放物品的最优解。
那么类似的,在树上我们加一维表示其所在子树的根节点,即dp[u][m]表示以u为根的子树选择m个城池的最优解。又因为这里涉及到了合并子树的问题,我们就有下面的转移方程:
for(int j=size[u];j>=1;j--)//这里的size[u]不包括size[v]
for(int k=1;k<=size[v];k++)
dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]);
其中dp[i][1~m+1]的初值就是其本身的权值(之所以要+1是因为题目给我们的是一颗森林,我们用0号节点作为根节点)。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200;
struct edge
{int v,next;
}ed[N+5];
int head[N+5],num;
int n,m;
int dp[N+5][N+5],size[N+5];
void build(int u,int v)
{ed[++num].v=v;ed[num].next=head[u];head[u]=num;
}
void dfs(int u)
{size[u]=1;for(int i=head[u];i!=-1;i=ed[i].next){int v=ed[i].v;dfs(v);for(int j=size[u];j>=1;j++)for(int k=1;k<=size[v];k++)dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]);size[u]+=size[v];}
}
int main()
{while(~scanf("%d%d",&n,&m)){if(n==0&&m==0)break;memset(head,-1,sizeof(head));num=0;memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++){int u,w;scanf("%d%d",&u,&w);build(u,i);for(int j=1;j<=m+1;j++)dp[i][j]=w;}dfs(0);printf("%d\n",dp[0][m+1]);}return 0;
}
这篇关于HDU1561 The more, The Better 解题报告【树上DP/背包】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!