洛谷 P1776 宝物筛选 题解

题目大意

洛谷 P1776 宝物筛选

现有一个最大载重为 \(W\) 的采集车,有 \(n\) 种物品,每种物品价值为 \(v_i\),重量为 \(w_i\),件数为 \(m_i\)。问在采集车不超重的前提下能装入采集车的物品价值和最大为多少。

朴素做法

对于 0-1 背包来说,其状态转移方程为 \(dp_{j}=\max(dp_{j},dp_{j-v_i}+w_i)\),其中 \(i\) 为物品,\(j\) 为背包容量。而现在我们可以在原来基础上再增加一层循环,遍历装入几件该物品。

时间复杂度 \(O(W\sum{m_i})\),预计得分 50pts,代码如下。

#include<bits/stdc++.h>
using namespace std;

const int N=105,maxW=4e4+10;
int n,W;
int v[N],w[N],m[N],dp[maxW];

int main(){
    scanf("%d%d",&n,&W);
    for (int i=1;i<=n;++i) scanf("%d%d%d",v+i,w+i,m+i);
    for (int i=1;i<=n;++i){
        for (int j=W;j>=w[i];--j){
            for (int k=1;k<=m[i] && j>=k*w[i];++k) dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);
        }
    }
    printf("%d",dp[W]);
    return 0;
}

过渡

前一个思路无法再优化了。我们能否将多重背包转化为 0-1 背包呢?考虑拆物品。我们可以将第 \(i\) 件物品拆成 \(m_i\) 件价值 \(v_i\),重量 \(w_i\) 的物品,时间复杂度仍为 \(O(W\sum{m_i})\)

二进制优化

注意到之前过渡做法会 TLE,主要原因是物品数量过多,有 \(\sum{m_i}\) 件,可不可以减少物品数量呢?由于每个数都对应一个二进制数,考虑拆成 \(2\) 的次方件。

对于当前物品数量 \(m_i\) 来说,若 \(p=\lfloor\log_2{m_i}\rfloor\),由于 \(m_i-1-2-\dots-2^{p-1}=m_i-2^p+1\) ,所以可以把此物品拆成 \(1,2,\dots,2^{p-1},m_i-2^p+1\)。如果装入 \(1\sim 2^p-1\) 件,都能用前 \(p-1\) 个元素表示出来。

若装入件数在 \(2^p\sim m_i\) 之间,因为 \(2^p\) 为小于 \(m_i\) 的最大的 \(2\) 的次方,所以 \(2^p\le m_i<2^{p+1}\),对于 \(m_i\in \mathbb{Z}\)\(m_i\le 2^{p+1}-1\),移项可得 \(m_i-2^p+1\le 2^p\)。所以对于这一部分的件数,它们可以在 \(m_i-2^p+1\) 的基础上用前面的元素表示出来。

所以我们有了思路。遍历每个元素,先将其按上述方法拆分,再枚举个数套入背包模板,时间复杂度 \(O(W\sum(\log m_i))\)。代码如下。

#include<bits/stdc++.h>
using namespace std;

const int N=105,maxW=4e4+10;
int n,W;
int v[N],w[N],m[N],dp[maxW];

int main(){
    scanf("%d%d",&n,&W);
    for (int i=1;i<=n;++i) scanf("%d%d%d",v+i,w+i,m+i);
    for (int i=1;i<=n;++i){
        int p=0;
        for (int j=2;j<=m[i];j*=2) ++p;
        for (int j=(1<<(p-1));j>0;j>>=1){
            for (int k=W;k>=j*w[i];--k) dp[k]=max(dp[k],dp[k-j*w[i]]+j*v[i]);
        }
        int q=m[i]-(1<<p)+1;
        if (q!=0){
            for (int j=W;j>=q*w[i];--j) dp[j]=max(dp[j],dp[j-q*w[i]]+q*v[i]);
        }
    }
    printf("%d",dp[W]);
    return 0;
}
posted @ 2025-12-22 16:06  CodingJuRuo  阅读(1)  评论(0)    收藏  举报