前面已经回顾了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) |
当然,输入上的区别就不说了。
========================================
二进制思想
问题描述:
假设有1000个苹果,现在要取n个苹果,如何取?正常的做法应该是将苹果一个一个拿出来,直到n个苹果被取出来。
又假设有1000个苹果和10只箱子,如何快速的取出n个苹果呢?可以在每个箱子中放 2^i (i<=0<=n)个苹果,也就是 1、2、4、8、16、32、64、128、256、489(最后的余数),相当于把十进制的数用二进制来表示,取任意n个苹果时,只要推出几只箱子就可以了。
多重背包二进制拆分实现:
跟完全背包一样的道理,利用二进制的思想将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]间的每一个整数,均可以用若干个系数的和表示。
代码如下:测试用例见代码末的注释
#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:
多重背包:1014、1276、1742、2392、3260(完全+多重)
本文相关代码可以到这里下载。
文章来源:勇幸|Thinking (http://www.ahathinking.com) 转载请注明,谢谢合作。
(全文完)