P9140 [THUPC 2023 初赛] 背包 题解
题目描述
\(n\) 种物品,第 \(i\) 种体积 \(v_i\) ,价值 \(c_i\) 。
\(q\) 次询问,给定 \(V\) ,你需要选择若干个物品(可以相同),使得体积和恰好为 \(V\) ,要求最大化价值和,无解输出 -1 。
数据范围
- \(1\le n\le 50,1\le v_i\le 10^5,1\le c_i\le 10^6,1\le q\le 10^5,10^{11}\le V\le 10^{12}\) 。
时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
对于体积非常大的完全背包,容易发现大多数都在取 "性价比" 最高的一个物品。
记 \(\frac{c_i}{v_i}\) 最大的一个物品体积为 \(m\) ,价值为 \(w\) 。
如果其他物品得到的背包方案为 \((V',C')\) ,则答案为 \(C'+\frac{V-V'}mw\) 。
注意其他任何一个物品都不会取超过 \(m\) 个,否则替换成 \((m,w)\) 更优。
因此 \(V'\le 10^{10}\) ,结合 \(V\ge 10^{11}\) 的限制,我们只需要最大化 \(C'-\frac{V'}mw\) 。
对其他每个物品 \((v_i,c_i)\) ,连边 \(\big(x,(x+v_i)\%m,c_i-\lfloor\frac{x+v_i}m\rfloor\cdot w\big)\) ,跑最长路即可。
然而边权可能为负, spfa 的时间复杂度又是错的,虽然原题数据很水可以通过。
注意到 \(v_i\) 不会选择超过 \(m\) 个,因此在环上以 \(v_i\) 为步长,转两圈一定可以覆盖所有的转移。
时间复杂度 \(\mathcal O(nm)\) 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5,inf=1e18;
int n,p=1,q,x;
int c[maxn],v[maxn];
int f[maxn];
signed main()
{
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&v[i],&c[i]);
if(1.0*c[i]/v[i]>=1.0*c[p]/v[p]) p=i;
}
int m=v[p],w=c[p];
for(int i=0;i<m;i++) f[i]=-inf;
f[0]=0;
for(int i=1;i<=n;i++)
{
if(i==p) continue;
for(int j=0,d=__gcd(v[i],m),lim=2*m/d;j<d;j++)///d个环,环长为m/d
for(int k=1,x=j;k<=lim;k++)
{
int y=(x+v[i])%m;
f[y]=max(f[y],f[x]+c[i]-(x+v[i])/m*w),x=y;
}
}
while(q--)
{
scanf("%lld",&x);
printf("%lld\n",max(f[x%m]+x/m*w,-1ll));
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17365531.html
浙公网安备 33010602011771号