题解:P4095 [HEOI2013] Eden 的新背包问题

首先我们知道如果没有去掉玩偶的限制就是一个二进制分组优化的完全背包,这是基础所以不讲。
然后我们来看这个去掉玩偶的操作。对于这种删除操作一般有以下几种做法:

  1. 如果可以直接算贡献就直接算
  2. 否则如果加入操作好维护的话就反过来处理
  3. 最后都不好维护的话就考虑能不能预处理一些信息,然后能拼出我们想要的答案。

显然我们要用第 \(3\) 种方法。在转移中删除显然就是跳过这个不考虑。一般情况下这种可以用前缀和过去,但是这里不行。(因为转移需要依赖前面的信息,所以答案后半段都会受到本该被删除的这个信息的影响)
但是注意到我们有一个非常好的性质那就是无论顺序是怎么样的我们算出来的答案都一样。再回想一下我们的要求:答案前半段不受后面的影响,后半段不受前面的影响。
然后我们就做出来了。正着做一遍背包,反着做一遍,计算答案的时候枚举前半段用掉的价值,剩下的就是后半段的。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n , dp[1005][1005] , dp2[1005][1005] , m = 1000;
struct item{
	int x , y , z;
}a[1005];
void solve(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)dp[i][j] = dp[i-1][j];
		int r = a[i].z , p = 1;
		while(r>=p){
			for(int j=m;j>=a[i].x*p;j--){
				dp[i][j] = max(dp[i][j],dp[i][j-a[i].x*p]+a[i].y*p);
			}
			r -= p , p <<= 1;
		}
		if(r){
			for(int j=m;j>=a[i].x*r;j--){
				dp[i][j] = max(dp[i][j],dp[i][j-a[i].x*r]+a[i].y*r);
			}
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=1;j<=m;j++)dp2[i][j] = dp2[i+1][j];
		int r = a[i].z , p = 1;
		while(r>=p){
			for(int j=m;j>=a[i].x*p;j--){
				dp2[i][j] = max(dp2[i][j],dp2[i][j-a[i].x*p]+a[i].y*p);
			}
			r -= p , p <<= 1;
		}
		if(r){
			for(int j=m;j>=a[i].x*r;j--){
				dp2[i][j] = max(dp2[i][j],dp2[i][j-a[i].x*r]+a[i].y*r);
			}
		}
	}
}
signed main(){
	cin >> n;
	for(int i=1;i<=n;i++)cin >> a[i].x >> a[i].y >> a[i].z;
	solve();
	int p;cin >> p;
	while(p--){
		int d , e , ans = 0;cin >> d >> e;d++;
		for(int i=0;i<=e;i++)ans = max(ans,dp[d-1][i]+dp2[d+1][e-i]);
		cout << ans << "\n";
	}
	return 0;
}
posted @ 2026-06-18 15:15  虚空远行者  阅读(4)  评论(0)    收藏  举报