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)要优秀的多

 

posted @ 2021-07-19 15:46  Mint-hexagram  阅读(74)  评论(0编辑  收藏  举报