[THUPC 2023 初赛] 背包

[THUPC 2023 初赛] 背包

虚假的 NP-Hard 问题。

题意

\(n\) 种物品。第 \(i\) 种物品有无穷多个,单个物品体积为 \(v_i\),价值为 \(c_i\)

接下来有 \(q\) 次询问。每次询问给定背包的容积 \(V\),你需要选择一些物品恰好装满背包,并最大化所选物品的总价值。如果不存在合法方案,输出 -1

\(1 \leq n \leq 50\)\(1 \leq v_i \leq 10^5\)\(1 \leq c_i \leq 10^6\)\(1 \leq q \leq 10^5\)\(10^{11} \leq V \leq 10^{12}\)

思路

对于背包问题,有一种贪心做法,也就是每次选取性价比最高的物品。但这种做法显然是假的,因为可能会有剩余空间无法利用。

观察数据范围,发现 \(V\) 的下界很大。此时未使用的体积相对于 \(V\) 来说是极少的。所以,大部分的体积应该被性价比最高的物品填充。

假设性价比最高的物品体积为 \(v\),价值为 \(c\)。则我们可以构建 \(\bmod \space v\) 的同余最短路模型,跑最长路即可。

此时我们不知道性价比最高的物品能选几个,所以我们考虑修改边权再跑最长路。

关于 SPFA,它死了。所以我们需要使用别的方法求出最短路。

考虑图的特殊性。发现对于同一个物品,在图上的转移构成若干个环。对于一个环,我们最多只需要转两圈就可以松弛完毕,否则图中存在正环。所以这么做的复杂度应该是 \(O(nv)\) 的。

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const long long INF=-4485090715960753727;
int v[60],id;
long long c[60],dis[100000];
int main(){
	int n,q;
	scanf("%d %d",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%d %lld",&v[i],&c[i]);
		if(c[i]*v[id]>=c[id]*v[i]){
			id=i;
		}
	}
	memset(dis,-0x3f,sizeof(dis));
	dis[0]=0;
	for(int i=1;i<=n;i++){
		if(i!=id){
			int cnt=__gcd(v[i],v[id]);
			for(int j=0;j<cnt;j++){
				int pos;
				pos=j;
				do{
					if(dis[pos]!=INF) dis[(pos+v[i])%v[id]]=max(dis[(pos+v[i])%v[id]],dis[pos]+c[i]-(pos+v[i])/v[id]*c[id]);
					pos=(pos+v[i])%v[id];
				}while(pos!=j);
				pos=j;
				do{
					if(dis[pos]!=INF) dis[(pos+v[i])%v[id]]=max(dis[(pos+v[i])%v[id]],dis[pos]+c[i]-(pos+v[i])/v[id]*c[id]);
					pos=(pos+v[i])%v[id];
				}while(pos!=j);
			}
		}
	}
	while(q--){
		long long V;
		scanf("%lld",&V);
		if(dis[V%v[id]]==INF) printf("-1\n");
		else printf("%lld\n",dis[V%v[id]]+V/v[id]*c[id]);
	} 
	return 0;
}
posted @ 2025-06-01 11:43  Oken喵~  阅读(0)  评论(0)    收藏  举报