一个超级奇怪的树形dp。因为儿子管父亲。
思路是显然的:f[i][j][k],表示第I个物品,用于合成(上缴)的有J个,花费K的代价能获得的最大价值。
我们还可以求出物品的获得上限(没钱or没子物品)
接下来就是一个背包了。
最外层枚举当前物品取几个
里面枚举儿子,用M^2时间可以跑出背包。
辅助数组G[i][j]前i个儿子花费j的代价能得到的最优值
然后枚举私吞的个数。
但是,数据中有森林的情况。
必须要加一个虚根,或者跑一个类似于G数组的dp
因此无脑贡献3WA
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
struct edge{int to,next,v;}e[20005];
int a[55],b[55],p[55],f[55][105][2005],g[55][2005],head[55],deg[55];
char ch[5]; int n,m,x,y,z,tot,ans,cnt,h[55][2005];
void ins(int u,int v,int w){
e[++tot].to=v;
e[tot].v=w;
e[tot].next=head[u];
head[u]=tot;
deg[v]++;
}
void dp(int x){
if (!head[x]){
b[x]=min(b[x],m/a[x]);
for (int i=0;i<=b[x];i++)
for (int j=i;j<=b[x];j++)
f[x][i][j*a[x]]=(j-i)*p[x];
return;
}
b[x]=inf;
for (int i=head[x];i;i=e[i].next){
dp(e[i].to);
b[x]=min(b[x],b[e[i].to]/e[i].v);
a[x]+=e[i].v*a[e[i].to];
}
b[x]=min(b[x],m/a[x]);
memset(g,233,sizeof(g));
g[0][0]=0;
for (int l=b[x];l>=0;l--){
int tot=0;
for (int i=head[x];i;i=e[i].next){
tot++;
for (int j=0;j<=m;j++)
for (int k=0;k<=j;k++)
g[tot][j]=max(g[tot][j],g[tot-1][j-k]+f[e[i].to][l*e[i].v][k]);
}
for (int j=0;j<=l;j++)
for (int k=0;k<=m;k++)
f[x][j][k]=max(f[x][j][k],g[tot][k]+p[x]*(l-j));
}
}
int main(){
memset(f,233,sizeof(f));
memset(h,233,sizeof(h));
h[0][0]=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d%s",&p[i],ch);
if (ch[0]=='A'){
scanf("%d",&x);
while (x--){
scanf("%d%d",&y,&z);
ins(i,y,z);
}
}
else scanf("%d%d",&a[i],&b[i]);
}
for (int x=1;x<=n;x++)
if (!deg[x]){
dp(x);
cnt++;
for (int i=0;i<=m;i++)
for (int j=0;j<=i;j++)
for (int k=0;k<=b[x];k++)
h[cnt][i]=max(h[cnt][i],h[cnt-1][j]+f[x][k][i-j]);
}
ans=0;
for (int i=0;i<=m;i++) ans=max(ans,h[cnt][i]);
printf("%d\n",ans);
}