[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;
}

浙公网安备 33010602011771号