状态非常好想,dp[i][j]表示以i为根的子树选j个叶子所能产生的最大价值。
这是个树上背包,方程好想,不太好写。
for(int k=sz[x];k;k--)
for(int j=1;j<=sz[v];j++)
dp[x][k]=max(dp[x][k],dp[x][k-j]+dp[v][j]-b[i].d);
一定要注意for的顺序,k从大到小for。
然后放在树上……
#include<iostream>
#include<cstdio>
#define maxn 3005
using namespace std;
struct E{
int to,nxt,d;
}b[maxn];
int fst[maxn],tot;
void build(int f,int t,int d)
{
b[++tot]=(E){t,fst[f],d};
fst[f]=tot;
}
int val[maxn];
int sz[maxn];
int n,m;
void dfs(int x)
{
if(x>n-m)
{sz[x]=1;return ;}
for(int i=fst[x];i;i=b[i].nxt)
{
int v=b[i].to;
dfs(v);sz[x]+=sz[v];
}
}
int dp[maxn][maxn];
int a[maxn];
void f(int x)
{
dp[x][0]=0;
if(x>n-m)
{
dp[x][1]=val[x];
return ;
}
for(int i=fst[x];i;i=b[i].nxt)
{
int v=b[i].to;
f(v);
for(int k=sz[x];k;k--)
for(int j=1;j<=sz[v];j++)
dp[x][k]=max(dp[x][k],dp[x][k-j]+dp[v][j]-b[i].d);
}
}
int main()
{
scanf("%d%d",&n,&m);
int x,u,d;
for(int i=1;i<=n-m;i++)
{
scanf("%d",&x);
for(int j=1;j<=x;j++)
{
scanf("%d%d",&u,&d);
build(i,u,d);
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j]=-1e9;
for(int i=n-m+1;i<=n;i++)
scanf("%d",&val[i]);
int p=m,t;
dfs(1);f(1);
for(t=p;t>=0;t--)
if(dp[1][t]>=0) break;
printf("%d",t);
}

本文介绍了一种树形结构上的动态规划问题——树上背包问题,并给出了具体的实现代码。通过递归的方式,利用DP数组记录以某个节点为根的子树中选择不同数量叶子节点的最大价值。
773

被折叠的 条评论
为什么被折叠?



