peiwenjun's blog 没有知识的荒原

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

posted on 2023-04-30 17:34  peiwenjun  阅读(18)  评论(0)    收藏  举报

导航