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/

  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——81
    《那些年啊,那些事——一个程序员的奋斗史》——83
    《那些年啊,那些事——一个程序员的奋斗史》——80
    《那些年啊,那些事——一个程序员的奋斗史》——82
    《那些年啊,那些事——一个程序员的奋斗史》——81
    《那些年啊,那些事——一个程序员的奋斗史》——82
    《那些年啊,那些事——一个程序员的奋斗史》——82
    网络学习杂七杂八
    字典类的代码的学习
    SNMP++ 编译记录
  • 原文地址:https://www.cnblogs.com/sunshisonghit/p/4396605.html
Copyright © 2011-2022 走看看