多重背包 II —— 二进制优化

多重背包 II —— 二进制优化

核心思想:转换成0~1背包问题。———— 对个体拆分后全部打散,反正能够保证从全局上使得所有情况依然存在就可以了。

  1. 核心思想前提建议先把多重背包朴素版算法搞清楚

    转换成0 ~ 1背包问题,对个体拆分后全部打散,反正能够保证从全局上使得所有情况依然存在就可以了。

  2. 举例分析(以下都使用该样例)

    在选取第i种物品时,该物品有134件,二进制划分为**1,2,4,8,16,32,64,134 - (1 + 2 + 4 + 8 + 16 + 32 + 64) = 7 **这几个数字,这几个数字就相当于0 ~ 1背包问题中的每种背包。

    仔细观察可知0 ~ 134内的任一数字均可由以上划分数字组合相加构成。这样即便打散后在宏观全局上仍然能够满足0~134的每种情况。

  3. 字母标准化

    在选取第i种物品时,该物品有s件,二进制划分为1,2,4,8,16,······,2k-1,2k,s - (1 + 2 + ··· + 2k)这几个数字,这几个数字就相当于0 ~ 1背包问题中的每种背包。

    仔细观察可知0 ~ s内的任一数字均可由以上划分数字组合相加构成。这样即便打散后在宏观全局上仍然能够满足0 ~ s的每种情况。

  4. 问题(只是几个帮助理解的小问题,上面已经理解的话可自行跳过)

    1. 问:在134被划分成1,2,4,8,16,32,64后已经可以组合出7 = 1+ 2 + 4了,为什么最后面还要搞个7?

      答:现在已经转换成0 ~ 1背包来考虑问题了,那么前面1,2,4这几个数字已经被用来组合成1 + 2 + 4 + 8 + 16 + 32 + 64 = 127了,就已经不能再使用了。这时候要是想要再组合成134那么必然还要在127的基础上再加一个7。

    2. 问:0 ~ 127的数字都可以凑满了,134也可以凑出来了,那么128 ~ 133该怎么凑?

      答:因为上面要凑134的时候又引入了一个数字7,并且0 ~ 127不需要这个7就可以轻松凑出来的,那么就有如下:

      133 = 126 + 7;

      132 = 125 + 7;

      131 = 124 + 7;

      ········

      ········

  5. 代码示例:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 11000;
    int n, m;
    int v[N], w[N], f[2010];
    
    int main(){
        scanf("%d%d", &n, &m);
        int cnt = 0;
        for(int i = 1; i <= n; i ++){
            //  v  w  s
            int a, b, s;
            scanf("%d%d%d", &a, &b, &s);
            int k = 1;
            while(k <= s){
                cnt ++;
                v[cnt] = k * a;
                w[cnt] = k * b;
                s -= k;
                k *= 2;
            }
            if(s > 0){// s只能 >0 或者 =0
                cnt ++;
                v[cnt] = s * a;
                w[cnt] = s * b;
            }
        }
        for(int i = 1; i <= cnt; i ++)
            for(int j = m; j >= v[i]; j --){
                f[j] = max(f[j], f[j - v[i]] + w[i]);
            }
        printf("%d", f[m]);
        return 0;
    }
    
posted @ 2022-01-28 10:26  ture?  阅读(234)  评论(0编辑  收藏  举报