https://www.luogu.org/problemnew/show/P1273
C++版本一
树状DP
我们设dp[i][j]表示在以i为根的子树中,满足j个客户的需求所能获得的最大收益,
那么在最终求最多客户时,只要求最大的dp[1][i]>=0的i就行了。
至于分组背包,我们设dp[i][u][j]表示以u为根的子树,仅用前i个儿子,满足j个客户取得最大价值,
那么dp[i][u][j]=max(dp[i-1][u][j-k]+dp[full_son_size[v]][v][k]);
而i这一维可以直接用滚动数组优化掉。
而这个背包,有些细节优化是可以过掉讨论中的那个极限数据。
如,将下面题解代码中sum+=x改到dp后。然后把dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k])
改为dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]),最后把dp[u][j]先用一个数组t保存一下就行了。
/*
*@Author: STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=3000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,q;
int ans,cnt,flag,temp,a,c;
int v[N];
char str;
vector<int>G[N];
int w[N];
int dp[N][N];
int dfs(int rt){
dp[rt][0]=0;
if(rt>n-m){
dp[rt][1]=v[rt];
return 1;
}
int res=0;
for(int i=0,j=G[rt].size();i<j;i++){
int u=rt;
int v=G[rt][i];
int tmp=dfs(v);
res+=tmp;
for(int k=res;k>0;k--){
int minl=min(tmp,k);
for(int l=1;l<=minl;l++){
dp[u][k]=max(dp[u][k],dp[u][k-l]+dp[v][l]-w[v]);
}
}
}
return res;
}
int main()
{
#ifdef DEBUG
freopen("input.in", "r", stdin);
//freopen("output.out", "w", stdout);
#endif
scanf("%d%d",&n,&m);
//scanf("%d",&t);
//while(t--){}
for(int i=1;i<=n-m;i++){
scanf("%d",&k);
for(int j=1;j<=k;j++){
scanf("%d%d",&a,&c);
G[i].push_back(a);
w[a]=c;
}
}
for(int i=n-m+1;i<=n;i++){
scanf("%d",&v[i]);
}
memset(dp,~0x3f,sizeof(dp));
dfs(1);
for(int i=m;i>=0;i--){
if(dp[1][i]>=0){
cout<<i<<endl;
break;
}
}
//cout << "Hello world!" << endl;
return 0;
}