题解: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;
}

常数可能有点大,需要加快读才能过。

posted @ 2025-02-09 12:45  hnczy  阅读(44)  评论(0)    收藏  举报