给出一颗树,树的根为0,占领每个节点都会获得一个价值,但是要得到某个子节点的价值必须先要占领这个子节点的直接前驱也就是父亲节点。问得到的最大价值是多少。
题目给出最多能占领m个点。
题解:
树形背包(01)设置状态dp[i][j] 表示以i为根j个节点能得到的最大价值(这里的j可以表示不含根节点有也可以表示含有根节点)
在转移时状态应该这样:dp[u][i] = max { dp[u][i] , dp[v][i-j] + dp[v][j] } 这里的dp[u][i]表示根节点为u有i个节点的最大价值,但是注意了,这里的i不包含u这个根节点,但是dp[v][j]中的j是包含v这个节点的。这样转移是为了满足题目说要先占领父亲节点才能占领儿子节点,注意看代码仔细理解这中方法的好处。在搜索函数的末尾要加上对dp[u][i]的更新,这里的i表示的是包含u这个节点没,更行要倒着更新防止后效性。
PS:其实这题也可以 这样定义状态 dp[i][j][2] 0表示不包含根节点i,1表示包含。这样做就是有点浪费内存而已,不过没什么差别。
#include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> #include<math.h> using namespace std; #define oo 0x3f3f3f3f #define maxn 205 int dp[maxn][maxn]; struct Node { int v,next; }E[maxn<<1]; int head[maxn],tol; int val[maxn]; int n,m; void inst() { memset(head,-1,sizeof head); tol=0; memset(dp,0,sizeof dp); } void add_edge(int u,int v) { E[tol].v=v; E[tol].next=head[u]; head[u]=tol++; } void tree_dp(int u) { for(int i=head[u];i!=-1;i=E[i].next) { int v=E[i].v; tree_dp(v); for(int j=m;j>=0;j--) for(int k=0;k<=j;k++) dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]); } for(int i=m+1;i>=1;i--) dp[u][i]=dp[u][i-1]+val[u]; } int main() { int u,v; while(scanf("%d %d",&n,&m)!=EOF) { if(n==0&&m==0) break; inst(); val[0]=0; for(int i=1;i<=n;i++) { scanf("%d %d",&v,&val[i]); add_edge(v,i); } tree_dp(0); printf("%d\n",dp[0][m+1]); } return 0; } /* */