ybt进阶:二进制优化(多重背包)
二进制优化用于多重背包中。
(1)多重背包模板:
long long dp[maxn], n, W, v[maxn], w[maxn], m[maxn]; //n是物品种类,W是背包总容量,v[i]是第i种物品的价值,w[i]是第i种物品的重量,m[i]是第i种物品的件数 for(int i = 1;i <= n;++i) { for(int j = W;j >= 0;--j) { for(int k = 1;k <= m[i] && w[i]*k <= j;++k) { dp[j] = max(dp[j],dp[j-w[i]*k]+v[i]*k); } } }
我们可以发现这里第三层(最内层)循环枚举了物品件数,使得这个算法的复杂度由完全背包或01背包的O(n^2)变为O(n^3),
在面对一些数据较大的题目肯定会被卡掉。
这样下去肯定是不行的(笑)。
因此我们可以考虑通过把si件物品合成几个小部分,当作单独的物品,把题目转化成01背包。
这就需要用到二进制拆分。
(2)二进制拆分(又称二进制优化)的具体内容及其证明
由于任何一个整数都可以用二进制表示。我们可以求出2^0+2^1+....+2^p <= m[i]的最大整数p,设R[i] = m[i] - 2^0 - 2^1-.....-2^p
<1>根据p的最大性,可以得出2^0+2^1+.....+2^(p+1) > m[i],通过联立和移项可以得出2^(p+1) > R[i],
因此,2^0,2^1,...2^p中任选几个相加可以表示出0~R[i]之间的任意一个整数。
<2>从2^0,2^1,....,2^p以及R[i]中选出若干个相加,可以表示出R[i] ~ R[i] + 2^p - 1之间的任何整数,
因为R[i] + 2^p - 1 = m[i],因此2^0,2^1,.....2^p,R[i]中选出若干个相加能表示出R[i]到M[i]的任何整数
总结一下,就是证明分解后与分解前完全等效,没有任何区别(除了需要枚举的次数减少了)。
综上,我们可以把m[i]件物品分解成p+2件物品,其费用分别为:
(2^0) * w[i],(2^1) * w[i],.....,(2^p) * w[i],R[i] * w[i]
这p+2个物品可以凑整0 ~ m[i] * w[i]之间所有能被w[i]整除的数,且不能凑成大于m[i] * R[i]的数。
这等价于原问题中体积为wi的物品可以被使用0~m[i]次。
(3)算法复杂度:把原有的m[i]个物品拆分成了logm[i]个,需要枚举的次数大大减少了。
因此复杂度就是:O(n^2 * logm[i])比O(n^3)要优秀的多