https://www.luogu.org/problemnew/show/P1273
思路:
dp[i][j]表示以i为根节点的子树让j个人听能获得最大的钱,接下来就是在树上进行01背包操作了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=3e3+10;
const int inf=0x3f3f3f3f;
struct node
{
int v,w;
};
vector<node>e[maxn];
int dp[maxn][maxn];
int n,m;
int c[maxn];
int sz[maxn];
int in[maxn];
void dfs(int x,int pre)
{
if(in[x]==1)
{
dp[x][1]=c[x];
sz[x]=1;
return;
}
dp[x][0]=0;
for(int i=0;i<e[x].size();i++)
{
int v=e[x][i].v;
int w=e[x][i].w;
if(v==pre)continue;
dfs(v,x);
sz[x]+=sz[v];
for(int j=sz[x];j>=1;j--)
{
for(int k=1;k<=min(j,sz[v]);k++)
{
dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[v][k]-w);
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
int k,y,w;
node q;
memset(dp,-inf,sizeof(dp));
for(int i=1;i<=n-m;i++)
{
scanf("%d",&k);
for(int j=1;j<=k;j++)
{
scanf("%d%d",&y,&w);
q.v=y,q.w=w;
e[i].push_back(q);
in[i]++;
in[y]++;
q.v=i,q.w=w;
e[y].push_back(q);
}
}
for(int i=n-m+1;i<=n;i++)
{
scanf("%d",&c[i]);
}
dfs(1,-1);
for(int i=m;i>=0;i--)
{
if(dp[1][i]>=0)
{
printf("%d\n",i);
break;
}
}
return 0;
}