题目链接:https://vjudge.net/problem/HDU-1561 题意:给一个森林,每个结点有个权值,求选m个结点的最大权值和,并且选子结点前必须先选父结点。 思路: 把每颗树的树根连在0号结点上,那
题目链接:https://vjudge.net/problem/HDU-1561
题意:给一个森林,每个结点有个权值,求选m个结点的最大权值和,并且选子结点前必须先选父结点。
思路:
把每颗树的树根连在0号结点上,那么就是一棵树了,最后求选m+1个结点的最大权值即可。状态很好想,用dp[u][j]表示在u的子树中选j个结点最大权值和,初始化dp[u][1]=a[u],a[u]是结点u的权值,则转移方程为:
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k])。
AC代码:
#include<cstdio> #include<algorithm> using namespace std; const int maxn=205; int n,m,cnt,head[maxn],a[maxn],dp[maxn][maxn]; int root[maxn],t,son[maxn]; struct node{ int v,nex; }edge[maxn]; void adde(int u,int v){ edge[++cnt].v=v; edge[cnt].nex=head[u]; head[u]=cnt; } void dfs(int u){ son[u]=1; dp[u][1]=a[u]; for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; dfs(v); son[u]+=son[v]; for(int j=son[u];j>=1;--j) for(int k=1;k<=min(j-1,son[v]);++k) dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]); } } int main(){ while(scanf("%d%d",&n,&m),n||m){ cnt=0; for(int i=0;i<=n;++i){ head[i]=0; for(int j=1;j<=n+1;++j) dp[i][j]=-1; } for(int i=1;i<=n;++i){ int u; scanf("%d%d",&u,&a[i]); adde(u,i); } dfs(0); printf("%d\n",dp[0][m+1]); } return 0; }