题目大意:给出一片森林,总共有n个点,并且都有权值。从中选出m个,使权值和最大。其中,选某个节点之前必须先选其父节点。
题目分析:给所有的树都加一个共同的权值为0的根节点,使森林变成一棵树。定义状态dp(u,k)表示在以节点u为根节点的组中选k个节点的最大权值。则状态转移方程为:
dp(u,k)=max(dp(u,k),dp(v,j)+dp(u,k-j),其中v是u的子节点。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; const int N=205; struct Edge { int to,nxt; }; Edge e[N<<1]; int n,m,cnt; int w[N]; int head[N]; int dp[N][N]; void add(int u,int v) { e[cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt++; } void init() { int fa; cnt=w[0]=0; memset(head,-1,sizeof(head)); memset(head,-1,sizeof(head)); for(int i=1;i<=n;++i){ scanf("%d%d",&fa,w+i); add(fa,i); } } void dfs(int u) { dp[u][1]=w[u]; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; dfs(v); for(int j=m+1;j>=2;--j){ for(int k=1;k<j;++k) dp[u][j]=max(dp[u][j],dp[u][k]+dp[v][j-k]); } } } int solve() { memset(dp,0,sizeof(dp)); dfs(0); return dp[0][m+1]; } int main() { while(scanf("%d%d",&n,&m)&&(n+m)) { init(); printf("%d\n",solve()); } return 0; }
时间: 2024-11-05 11:51:54