zoukankan      html  css  js  c++  java
  • (转)多重背包

    文章作者:Yx.Ac   文章来源:勇幸|Thinking (http://www.ahathinking.com)   转载请注明,谢谢合作。

    ---

    前面已经回顾了01背包完全背包,本节回顾多重背包的几种实现形式,主要有以下几方面内容:

    ==多重背包问题定义 & 基本实现

    ==多重背包二进制拆分实现

    ==防火防盗防健忘

    ========================================

    多重背包问题定义 & 基本实现

    问题:有个容量为V大小的背包,有很多不同重量weight[i](i=1..n)不同价值value[i](i=1..n)的货物,第i种物品最多有n[i]件可用,计算一下最多能放多少价值的货物。

    对于多重背包的基本实现,与完全背包是基本一样的,不同就在于物品的个数上界不再是v/c[i]而是n[i]与v/c[i]中较小的那个。状态转移方程如下

    1
    f(i,v) = max{ f(i-1,v-k*c[i]) + k*w[i] | 0<=k<=n[i] }

    代码与完全背包的区别仅在内部循环上由

    1
    for(k = 1; k <= j/weight[i]; ++k)

    变为

    1
    for(k = 1; k <=n[i] && k<=j/weight[i]; ++k)

    当然,输入上的区别就不说了。

    ========================================

    多重背包二进制拆分实现

    跟完全背包一样的道理,利用二进制的思想将n[i]件物品i拆分成若干件物品,目的是在0-n[i]中的任何数字都能用这若干件物品代换,另外,超过n[i]件的策略是不允许的。

    方法是将物品i分成若干件,其中每一件物品都有一个系数,这件物品的费用和价值都是原来的费用和价值乘以这个系数,使得这些系数分别为1,2,4,…,2^(k-1),n[i]-2^k+1,且k满足n[i]-2^k+1>0的最大整数。例如,n[i]=13,就将该物品拆成系数为1、2、4、6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示。

    代码如下:测试用例见代码末的注释

    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
    55
    56
    57
    58
    59
    #include <iostream>
    using namespace std;
     
    /* 多重背包 二进制拆分
     * Time Complexity  大于O(N*V)
     * Space Complexity O(N*V)
     * 设 V <= 200 N <= 10 ,拆分后 物品总数 < 50
     * 每件物品有 log n[i]种状态
     */
     
    int maxV[201];
    int weight[50]; /* 记录拆分后物体重量 */
    int value[50];  /* 记录拆分后物体价值 */
    int V, N;
     
    void main()
    {
        int i, j;
        scanf("%d %d",&V, &N);
        int weig, val, num;
        int count = 0;
     
        for(i = 0; i < N; ++i)
        {
            scanf("%d %d %d",&weig,&val,&num);
     
            for(j = 1; j <= num; j <= 1) // 二进制拆分
            {
                weight[count] = j * weig;
                value[count++] = j * val;
                num -= j;
            }
            if(num > 0)
            {
                weight[count] = num * weig;
                value[count++] = num * val;
            }
        }
        for(i = 0; i < count; ++i)  // 使用01背包
        {
            for(j = V; j >= weight[i]; --j)
            {
                int tmp = maxV[j-weight[i]] + value[i];
                maxV[j] = maxV[j] > tmp ? maxV[j] : tmp;
            }
        }
        printf("%d",maxV[V]);
    }
     
    /*
        【输入样例】
        4 20
        3     9     3
        5     9     1
        9     4     2
        8     1     3
        【输出样例】
        47
    */

    ========================================

    简单背包基础总结:

    回顾了3种简单背包后,有些思想慢慢体会,实践中,对于01背包和完全背包使用一维数组实现是最简便高效的,对于多重背包,最好就是输入时进行二进制拆分,然后使用01背包,这样比基本实现和在运算时再进行拆分要简捷的多。

    防火防盗防健忘:

    没事水一下:POJ  PKU

    01背包: 321136243628

    完全背包:125213842063

    多重背包:10141276174223923260(完全+多重)

    本文相关代码可以到这里下载。

    (全文完)

    参考资料:背包问题九讲 http://love-oriented.com/pack/

  • 相关阅读:
    LeetCode Count of Range Sum
    LeetCode 158. Read N Characters Given Read4 II
    LeetCode 157. Read N Characters Given Read4
    LeetCode 317. Shortest Distance from All Buildings
    LeetCode Smallest Rectangle Enclosing Black Pixels
    LeetCode 315. Count of Smaller Numbers After Self
    LeetCode 332. Reconstruct Itinerary
    LeetCode 310. Minimum Height Trees
    LeetCode 163. Missing Ranges
    LeetCode Verify Preorder Serialization of a Binary Tree
  • 原文地址:https://www.cnblogs.com/sunshisonghit/p/4396605.html
Copyright © 2011-2022 走看看