【训练题9:多重背包的二进制拆分】宝物筛选 | 洛谷 P1776


宝物筛选 | 洛谷 P1776

难度

绿 题 绿题 绿

题意

一个 普通 的多重背包
物品数 N N N 背包容积 V V V
每个物品有重量 w i w_i wi 价值 v i v_i vi 物品数 c i c_i ci
求装完之后的最大价值和

数据范围

1 ≤ n ≤ 100 1\le n\le 100 1n100
0 ≤ V ≤ 4 × 1 0 4 0\le V\le 4\times10^4 0V4×104
∑ c i ≤ 1 0 5 \sum c_i\le 10^5 ci105

思路

【普通方法】

  • 对于每个物品,可以选择 1 ∼ c i 1\sim c_i 1ci
  • 转移: d p [ j ] = max ⁡ { d p [ j ] , d p [ j − k ∗ w [ i ] ] + k ∗ v [ i ] } dp[j]=\max\{dp[j],dp[j-k*w[i]]+k*v[i]\} dp[j]=max{ dp[j],dp[jkw[i]]+kv[i]}
  • 时间复杂度 O ( N V ∑ c i ) O(NV\sum c_i) O(NVci)
  • 经过一个数量循环优化以及O2优化,可以勉强卡过去、、
  • (开启了O2优化之后的时间)在这里插入图片描述

【二进制拆分】

  • 重点来了!
  • 由于一个物品数量特别多,我们需要对其进行优化。
  • 假设一个物品有17个,我们可以把它拆成:17=1+2+4+8+2
  • 前面几个数分别是 2的幂次方 , 最后一个数字为 个数减去前面的幂次方的和(或者叫做拆分的补足)
  • 我们可以对这几个数字进行选取,于是可以选出所有[1,17]范围内的所有数字
  • 选取数字的过程,正好就是01背包选取物品的过程。
  • 这样,每个物品的个数 C i C_i Ci 被我们减少成 log ⁡ C i \log C_i logCi
  • 总体时间复杂度即为 O ( N V ∑ log ⁡ C i ) O(NV\sum\log C_i) O(NVlogCi)
  • (同样开启了O2优化的二进制拆分的时间)
    在这里插入图片描述
  • (无O2优化的正常代码时间)
    在这里插入图片描述

核心代码【普通多重背包】

时间复杂度 O ( N V ∑ C i ) O(NV\sum C_i) O(NVCi)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 4e4+50;

int v[MAX];
int w[MAX];
int cnt[MAX];
int dp[MAX];
int main()
{
    
    
    int N,W;
    cin >> N >> W;
    for(int i = 1;i <= N;++i){
    
    
        cin >>  v[i] >> w[i] >> cnt[i];
    }
    for(int i = 1;i <= N;++i){
    
    
        for(int j = W;j >= w[i];--j){
    
    
            for(int k = 1;k <= cnt[i];++k){
    
    
                if(j - k * w[i] < 0)break;
                dp[j] = max(dp[j],dp[j - k * w[i]] + k * v[i]);
            }
        }
    }
    cout << dp[W];
    return 0;
}

核心代码【二进制拆分】

时间复杂度 O ( N V ∑ log ⁡ C i ) O(NV\sum\log C_i) O(NVlogCi)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e5+50;

int v[MAX];
int w[MAX];
int dp[MAX];
int main()
{
    
    
    int N,W;
    int ct = 0;
    cin >> N >> W;
    for(int i = 1;i <= N;++i){
    
    
        int tx,ty,tz;
        cin >> tx >> ty >> tz;
        for(int j = 1;j <= tz;j <<= 1){
    
    
            v[++ct] = tx * j;
            w[ct]   = ty * j;
            tz -= j;
        }
        if(tz){
    
    
            v[++ct] = tx * tz;
            w[ct]   = ty * tz;
        }
    }
    for(int i = 1;i <= ct;++i){
    
    
        for(int j = W;j >= w[i];--j){
    
    
            dp[j] = max(dp[j],dp[j-w[i]] + v[i]);
        }
    }
    cout << dp[W];
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/109647978