[bzoj1017] [JSOI2008] 魔兽地图
树上背包。
设\(f[i][j][k]\)表示\(i\)节点有\(j\)个来合成高一级的装备,买\(i\)节点及其子树的装备共花了不大于\(k\)元可以得到的最大力量值。
对于每一颗树做\(tree~dp\)转移,如果是叶子节点就直接算,
否则先根据儿子的限制以及钱的限制处理出最多可以买几个,然后枚举当前这件装备做几个,设\(g[j]\)为花\(j\)元做\(i\)件当前的装备之后剩下的边角料的最大获利,这个可以枚举儿子之后类似于背包的转移。之后\(f\)根据\(g\)算一遍就行了。
#include<bits/stdc++.h>
using namespace std;
inline void read(int &x) {
int f=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f-=f;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
inline void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;
print(x/10);putchar((x%10)^48);
}
inline void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
int f[60][120][2005],g[2005],n,head[60],tot,m,v[120],c[120],lim[120],vis[120],ans[2001],deg[120];
struct edge{int to,nxt,w;}e[120];
void ins(int u,int v,int w) {e[++tot].to=v,e[tot].nxt=head[u],head[u]=tot,e[tot].w=w;}
void dfs(int x) {
if(vis[x]) return ;vis[x]=1;
if(!head[x]) {
lim[x]=min(lim[x],m/c[x]);
for(int i=lim[x];~i;i--)
for(int j=i;j<=lim[x];j++)
f[x][i][j*c[x]]=v[x]*(j-i);
return ;
}lim[x]=1e9;
for(int i=head[x];i;i=e[i].nxt) {
dfs(e[i].to);
lim[x]=min(lim[x],lim[e[i].to]/e[i].w);
c[x]+=c[e[i].to]*e[i].w;
}
lim[x]=min(lim[x],m/c[x]);
for(int i=lim[x];~i;i--) {
memset(g,-0x3f,sizeof g);g[0]=0;
for(int j=head[x];j;j=e[j].nxt)
for(int k=m;~k;k--) {
int t=-1e9;
for(int p=0;p<=k;p++)
t=max(t,g[k-p]+f[e[j].to][e[j].w*i][p]);
g[k]=t;
}
for(int j=0;j<=i;j++)
for(int k=0;k<=m;k++)
f[x][j][k]=max(f[x][j][k],g[k]+(i-j)*v[x]);
}
}
int main() {
read(n);read(m);memset(f,-0x3f,sizeof f);
for(int i=1;i<=n;i++) {
read(v[i]);char ch[2];scanf("%s",ch+1);
if(ch[1]=='A') {
int T;read(T);int x,y;
for(int j=1;j<=T;j++) read(x),read(y),ins(i,x,y),deg[x]++;
}else read(c[i]),read(lim[i]);
}
for(int i=1;i<=n;i++)
if(!deg[i]) {
dfs(i);//write(i);
for(int j=m;~j;j--)
for(int k=0;k<=j;k++)
ans[j]=max(ans[j],ans[j-k]+f[i][0][k]);
}
write(ans[m]);
return 0;
}