多重背包:二的幂次法

引入

    所谓多重背包问题,基本长一个样:一个背包,承量有限为W,有n种物体,第i种物体,价值Vi,占用重量为 Wi,且有Ci件,选择物品若干放入背包,使得总重量不超过背包的承重,求最大总价值。

解法

       我们可以先从一般的01背包的解法入手:倘若用01背包的解法解这道题,很容易想到,可以将其中的一种物品分为Ci个相同的物品,物品的总个数就是C1+C2+C3+....+Cn,在以01背包的方法进行处理,我们令物品的总个数为S,它的时间复杂度为O(S*W),而通常,S接近于n*n,显然,这样处理并不优。

       于是,我们来尝试优化,而他的转移的时间复杂度是O(1)的,所以我们只能优化状态枚举。显而易见的一点是,W那一维无法优化,我们就锁定了S那一维。基于01背包的思想,我们应该减少总个数,而物品种数是不变的,所以尝试去减少每种物品分为多个物品的个数,即使用尽量少的物品构成Ci个相同物品能构成的所有状态。我们想到用二的幂数可以表示出任意正整数,所以可以把1,2,4,8,....,2^k(2^k<Ci<=2^k+1)个相同的物品分别看作一个物品,然后用这k个物品去代替Ci个相同的物品,可以高效的处理此类问题。但在分作k个物品时,还存在一个小问题,我们举个例子来看:设Ci=18,我们分作1,2,4,8,16共五个,然而以全取为例,1+2+4+8+16=31>18;所以,为了保证其正确性,我们将16替换为3 (18-1-2-4-8=3)。处理好这个问题就可以了。此时时间复杂度为O(n*logn*W),这就是多重背包的logn解法。

       当然,还有O(n*W)的解法——单调队列优化,我在此次就不提了,但很快会另补一篇关于O(n*W)单调队列优化的博客,有兴趣的可以留意一下,或去找其他人的博客。

代码

       这个代码其实非常容易构建,只要理解后,动动笔,注意一下细节就可以了,此处只给出核心的伪代码(分作logCi个物品):

for (int j=1;;j*=2){
    if (C[i]>=j){
        w[num]=j*W;
        v[num]=j*V;
	Ci-=j;
	num++;
    }
    else{
	w[num]=C[i]*W;
	v[num]=C[i]*V;
	num++;
	break;
    }
}

       接下来只要加一个01背包即可。

参考

《背包九讲》,《算法导论》及“lfw的课堂笔记”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值