loading

P9140 [THUPC 2023 初赛] 背包

题意

\(n\) 个物品,体积 \(v_i\) 价值 \(w_i\),每个物品可被多次取。\(q\) 次询问背包容量 \(V\),求是否存在恰好装满背包的方案,若有解输出价值和的最大值。

\(n\le 50,q\le 10^5,v_i\le 10^5,w_i\le 10^6,10^{11}\le V\le 10^{12}\)

分析

这种容量极大的特殊背包问题一般考虑按照贪心策略(性价比)先一顿狂取再考虑别的。假设性价比最大的物品为 \(p\),其体积为 \(m\),考虑同于最长路,建出 \(0\sim m-1\)\(m\) 个点表示背包容量体积模 \(m\)\(i\),转移就是往里面添加物品,\(i\rightarrow (i+v_j)\bmod m\),由于这 \(m\) 个点不能表示出体积除 \(m\) 下取整的结果,我们考虑每走过一整个 \(m\) 就将边权减掉 \(w_p\),所以边权为 \(w_i-\lfloor\frac{v_i+p}{m}\rfloor\)(相当于费用提前计算了)。最终询问 \(V\) 的答案就是 \(\lfloor\frac{V}{m}\rfloor w_p+f_{V\bmod m}\)

注意 \(p\) 取的数量越多价值越大这个结论是错的。

由于是模意义下的完全背包问题,可以写转圈。具体而言,对于一件物品,其连成的边会形成 \(\gcd(m,v_i)\) 个环,而在这个环上直接做 dp 显然是错的,但是我们可以通过做两遍 dp 保证所有起点都被枚举了一遍。单次转移复杂度是 \(O(m)\) 的,因此总复杂度是确定性 \(O(nm)\)

int n,Q,m,v[maxn],w[maxn];
int f[maxn];
int vis[maxn];
inline void solve_the_problem(){
	n=rd(),Q=rd();
	rep(i,1,n)v[i]=rd(),w[i]=rd();
	int r=1;
	rep(i,2,n)if(w[i]*v[r]>w[r]*v[i])r=i;
	if(r^1)swap(v[r],v[1]),swap(w[r],w[1]);
	mem(f,~0x3f);
	f[0]=0,m=v[1];
	rep(i,2,n){
		rep(j,0,m-1)vis[j]=0;
		rep(j,0,m-1)if(!vis[j]){
			for(int p=j,nxt;vis[p]<=1;++vis[p],p=nxt){
				nxt=(p+v[i])%m;
				ckmx(f[nxt],f[p]+w[i]-(p+v[i])/m*w[1]);
			}
		}
	}
	while(Q--){
		int x=rd();
		if(f[x%m]<-1e18){PW;continue;}//f[x%m]可能为负
		write(f[x%m]+x/m*w[1]);
	}
}
posted @ 2025-09-09 13:35  dcytrl  阅读(22)  评论(0)    收藏  举报