树形dp板子。
传送门:GO
用dp[i][j]表示以i为根的子树中选取j个观众能赚到的最多钱(经典模型)。
那么,dp[i][j+k]=max(dp[son[i]][k]+dp[i][j]-w,dp[i][j+k]),意思是说,如果在子树中选了k个观众,那么答案就是(当前只选了j个的情况+选子树中k个观众的情况-这一段代价)。
确定一下枚举上界:初步估计,每一次选择观众量就是当前子树所含观众量,所以将dfs设计成返回子树所含观众量的类型,就可以处理枚举上界了。
对于节点u,外层枚举上界就是u的所有子树的前缀(需动态更新),内层上界就是当前子树大小。
考虑到转移式子,少的观众会对多的观众造成影响,所以倒序枚举,从多到少,就不会造成多余影响了。
其余的见代码吧。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int read(){ 4 int x=0,f=1; 5 char c=getchar(); 6 while(!isdigit(c)){ 7 if(c=='-') f=-1; 8 c=getchar(); 9 } 10 while(isdigit(c)){ 11 x=x*10+c-'0'; 12 c=getchar(); 13 } 14 return x*f; 15 } 16 const int N=3010; 17 int n,m,cnt; 18 int head[N<<1]; 19 int mon[N],f[N][N]; 20 struct edge{int to,next,w;}e[N<<1]; 21 void addedge(int from,int to,int w){e[++cnt]=(edge){to,head[from],w};head[from]=cnt;} 22 int dfs(int u){ 23 if(u>n-m){ 24 f[u][1]=mon[u]; 25 return 1; 26 } 27 int upz=0; 28 for(int i=head[u];i;i=e[i].next){ 29 int v=e[i].to,w=e[i].w; 30 int now=dfs(v); 31 for(int j=upz;j>=0;j--){ 32 for(int k=0;k<=now;k++){ 33 f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]-w); 34 } 35 } 36 upz+=now; 37 } 38 return upz; 39 } 40 int main(){ 41 n=read();m=read(); 42 for(int i=1,k;i<=n-m;i++){ 43 k=read(); 44 for(int j=1;j<=k;j++){ 45 int x=read(),y=read(); 46 addedge(i,x,y); 47 } 48 } 49 for(int i=n-m+1;i<=n;i++) mon[i]=read(); 50 memset(f,-0x3f,sizeof(f)); 51 for(int i=1;i<=n;i++) f[i][0]=0; 52 dfs(1); 53 for(int i=m;i>0;i--){ 54 if(f[1][i]>=0){ 55 printf("%d",i); 56 return 0; 57 } 58 } 59 return 0; 60 }