题解:P4037 [JSOI2008] 魔兽地图
一道非常综合的背包题。
我们发现每一个点的选择对它的父亲的合成有影响。
这样的话我们可以尝试把这个因素记下来。
\(f_{rt,i,j}\) 来表示 \(rt\) 及它的子树中选择 \(i\) 个作为合成的材料的话,花费 \(j\) 的最大战斗力。
这样的话我们就可以直接用多重背包来写了。
先枚举 \(rt\) 的合成数量,在对每一个子树枚举,多重背包更新即可。
这里最好在转移的时候再加一个 \(g\) 数组,来记一下在此合成数量的花费为 \(i\) 时的最大收益。
每次清空一下即可。
可能有森林的情况,所以要把每一个点都枚举过来。
代码还是比较短的,重要在多重背包的写法,可以借鉴一下。
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=55,inf =1e9;
int n,m,a[N],b[N],cnt[N],d[N],fa[N],f[55][105][2005],g[2005],ans;
//表示f[u][i][j] u这个子树 i的花费,j用于合成的最大贡献
char c;
void tomax(int &x,int y){if(x<y)x=y;}
vector<pii>e[N];
void dfs(int rt) {
for(auto p:e[rt]) {
int u=p.first,c=p.second;
dfs(u);
cnt[rt]=min(cnt[rt],cnt[u]/c);
b[rt]+=b[u]*c;
}
cnt[rt]=min(cnt[rt],m/b[rt]);
for(int i=0; i<=cnt[rt]; i++){//至多能拥有多少
int now=i*b[rt];
memset(g,0,sizeof g);
for(auto tmp:e[rt]) {
int u=tmp.first,c=i*tmp.second;//需要的数量
for(int k=m; k>=0; k--)
for(int o=0; o<=k; o++)
tomax(g[k],g[k-o]+f[u][c][o]);
}
for(int j=0; j<=i; j++)
for(int k=m; k>=now; k--)
tomax(f[rt][j][k-j*b[rt]],g[k-now]+(i-j)*a[rt]),tomax(ans,f[rt][j][k]);
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
cin>>a[i]>>c;
if(c=='A') {
scanf("%d",&d[i]);
cnt[i]=inf;
for(int j=1,num,id; j<=d[i]; j++) {
scanf("%d%d",&id,&num);
e[i].push_back({id,num});
fa[id]=i;
}
} else scanf("%d%d",&b[i],&cnt[i]);
}
for(int i=1;i<=n;i++)if(!fa[i])dfs(i);
cout<<ans;
return 0;
}
常数可能有点大,需要加快读才能过。

浙公网安备 33010602011771号