• 【背包九讲——多重背包问题】


    更好的阅读体验 \color{red}{更好的阅读体验} 更好的阅读体验


    3.1 模板题


    多重背包问题 I

    原题链接

    描述

    有 N 种物品和一个容量是 V 的背包。

    第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

    求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
    输出最大价值。

    输入格式
    第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

    接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0 0

    4 5
    1 2 3
    2 4 1
    3 4 3
    4 5 2
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出样例:

    输出样例:

    10
    
    • 1

    思想

    • 状态表示:
      • 集合:dp[i][j]表示前i个物品,总体积不超过j的价值
      • 属性:最大价值
    • 状态计算:
      • 不选第i个 物品:dp[i][j] = dp[i - 1][j]
      • 选第i个物品k个:dp[i][j] = dp[i - 1][j - k * v[i]] + k * w[i]
      • 集合属性为最大价值,故两种情况取max()
    • 当背包容量不够或者物品数量不够时,即j < k * v[i] || k > s[i]时,不能选择物品,反之可选

    代码

    #include 
    using namespace std;
    
    const int N = 1e3 + 10;
    
    int dp[N][N];
    
    int v[N], w[N], s[N];
    
    void solve(){
        
        int n, m;
        
        cin >> n >>m;
        
        for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i] >> s[i];
        
        for(int i = 1; i <= n; i ++){
            for(int j = 1; j <= m; j ++){
                for(int k = 0; k * v[i] <= j && k <= s[i]; k ++){  //枚举选择物品的个数
                    dp[i][j] = max(dp[i][j],dp[i - 1][j - k * v[i]] + k * w[i]);
                }
            }
        }
        
        cout << dp[n][m] << endl;
        
    }
    
    int main(){
        
        solve();
        
        return 0;
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    多重背包问题 II

    原题链接

    描述

    有 N 种物品和一个容量是 V 的背包。

    第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

    求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
    输出最大价值。

    输入格式
    第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

    接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0 0 0

    输出样例:

    4 5
    1 2 3
    2 4 1
    3 4 3
    4 5 2
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出样例:

    10
    
    • 1

    思想

    • 由于 S S S范围大,不可能逐一枚举,故采用二进制优化
    • 对于一组数 1 , 2 , 4 , 8 , 16 , 32 1,2,4,8,16,32 1,2,4,8,16,32,每个数只使用一次,可以凑成如下的数:
      • 1 , 2 1,2 1,2可以凑出 3 3 3,即 1 , 2 1,2 1,2可凑出 1 ∼ 3 1\sim 3 13之间的任意数
      • 则用 1 , 2 , 4 1,2,4 1,2,4可以凑出 1 ∼ 7 1\sim 7 17之间的任意数
      • 1 , 2 , 4 , 8 1,2,4,8 1,2,4,8可以凑出 1 ∼ 15 1\sim 15 115之间的任意数
      • 1 , 2 , 4 , 8 , 16 1,2,4,8,16 1,2,4,8,16可以凑出 1 ∼ 31 1\sim 31 131之间的任意数
      • 1 , 2 , 4 , 8 , 16 , 32 1,2,4,8,16,32 1,2,4,8,16,32可以凑出 1 ∼ 63 1\sim 63 163之间的任意数
      • 综上,使用 2 0 , 2 1 , 2 2 , … , 2 n 2^0,2^1,2^2,\dots,2^n 20,21,22,,2n可以凑出 1 ∼ 2 n + 1 − 1 1\sim 2^{n+1}-1 12n+11之间的任意整数
    • 对于一个数 17 17 17,若要用上述方法凑出 1 ∼ 17 1\sim 17 117之间的任意数:
      • 可选 1 , 2 , 4 , 8 1,2,4,8 1,2,4,8,每个数只用一次即可凑出 1 ∼ 15 1\sim 15 115
      • 由于最大只能凑到 15 15 15,故需添加一个数 2 ( 17 − 15 ) 2(17-15) 2(1715),来凑到 17 17 17
      • 即用 1 , 2 , 4 , 8 , 2 1,2,4,8,2 1,2,4,8,2即可凑出 1 ∼ 17 1\sim 17 117之间的任意一个数
    • 对于第i个物品,体积为v[i],价值为w[i],共有s[i]
    • 由二进制优化,我们可以将s[i]利用二进制拆分,用 1 , 2 , 4 , … 1,2,4,\dots 1,2,4,凑出s[i]
    • 即我们将物品i,拆分成了:
      • 一件体积为1 * v[i]的物品,价值为1 * w[i]的捆绑物品
      • 一件体积为2 * v[i]的物品,价值为2 * w[i]的捆绑物品
      • 一件体积为4 * v[i]的物品,价值为4 * w[i]的捆绑物品
      • … \dots
    • 由拆分出的这些新的捆绑物品,我们可以凑出 1 ∼ s [ i ] 1\sim s[i] 1s[i]之间的任意数目的i物品,不必逐一枚举进行选择
    • 由二进制拆分,原有的每个物品数量 S S S可以降为 l o g 2 S log_2^S log2S,时间复杂度大幅降低
    • 对于最终求解,可以采用01背包的一维优化求解

    代码

    #include 
    using namespace std;
    
    const int N = 1e6 + 10;
    
    int dp[N];
    
    int cnt;  //用于记录新的捆绑物品的下标
    
    int v[N], w[N];
    
    void solve(){
        
        int n, m;
        cin >> n >> m;
        
        for(int i = 1; i <= n; i ++){
            
            int a, b, s;
            cin >> a >> b >> s;
            
            int k = 1;  //k从1开始捆绑
            while(k < s){
                cnt ++;
                v[cnt] = k * a;  //捆绑物品的体积
                w[cnt] = k * b;  //捆绑物品的价值
                s -= k;
                k <<= 1;
            }
            if(s){  //最后剩余的无法凑整的数
                cnt ++;
                v[cnt] = s * a;
                w[cnt] = s * b;
            }
            
        }
        
        for(int i = 1; i <= cnt; i ++){  //01背包一维优化求解
            for(int j = m; j >= v[i]; j --){
                dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
            }
        }
        
        cout << dp[m] << endl;
        
    }
    
    int main(){
        
        solve();
        
        return 0;
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

  • 相关阅读:
    连续六个季度实现盈利改善,达达集团内外双重确定性凸显
    2023山东科技大学考研介绍
    [附源码]SSM计算机毕业设计超市订单管理系统JAVA
    更新UpdatePanel外部控件
    C++ 二级指针与 const 关键字
    玩转 PI 系列-看起来像服务器的 ARM 开发板矩阵-Firefly Cluster Server
    二分查找的详解
    麒麟KYLINOS通过命令行配置kysec的防火墙
    Springcloud实战之自研分布式id生成器
    Spring request工具类
  • 原文地址:https://blog.csdn.net/LYS00Q/article/details/126328000