背包问题——二进制优化

本文深入探讨了二进制优化在解决背包问题中的应用。通过具体实例解释了如何利用二进制思想减少状态转移次数,从而提高算法效率,并讨论了在多重背包问题中的特殊处理方法。

为什么需要二进制

如果不使用二进制优化,当数据量变得很大的时候会出现超时的问题。

怎样使用二进制优化?

此处贴一段代码,感谢原作者,我稍微改了一下代码风格

for (int i = 0; i < A.size(); ++i){
    int j = 1;
    while(j * A[i] < m){
        for (int k = m; k >= j * A[i]; --k){
            f[k] = max(f[k], f[k - j * A[i]] + j * V[i]j;
        }
        j = j << 1;
    }
}

这里的" i “的作用是选择要放入的背包的种类,“ m ”是背包容量,” j "用于二进制优化(就是把物品变成物品块),while内的for循环是用于遍历背包容量
while做出了一个限制,保证目前的背包快不会超过背包的最大容量
然后进入for循环,这里的k用于遍历所有的背包容量,当k的值小于物品块的时候,退出循环。
然后将“ j ”按二进制左移一位,相当于✖2,得到了新的物品块

那么肯定有读者要问了:你怎么确定二进制物品块能够遍历所有的可能值呢?其实笔者最初也有这样的问题,拿3举例子:比如说现在" j "等于1,意味着我手中的的物品块刚好是一个物品,满足while的条件,进入for循环,当循环结束时,容量从(1*A[i]~m)的背包里都会放入1个物品,然后退出for,j左移1位,物品块增加2倍,满足while循环,进入for。这不就有了原来的一个物品块+现在的2个物品块=3个物品块了吗?对于5,7或者别的数字,同理。假设现在一共已经放入了3个物品块(1+2),然后我要放入4个物品块,那么因为f[k - j * A[i]] + j * V[i]j,当体积不那么大的时候,我还得空出一个物品的体积。
以7为例,二进制可以拆出001,010,100,那么根据程序,我们可以知道先放入1,这样1被遍历到了,再放入2,要是装不下,2被遍历,装得下,3被遍历,再放入4,装不下,4被遍历,装的下5被遍历(1+4)、6被遍历(2+4)、7被遍历(1+2+4),这样一来,1、2、3、4、5、6、7都被遍历过了,但是时间却被大大的节省了!
至于为什么不直接用最大容量的背包去和最大块的物品块比较呢?因为受到状态转移方程的限制,后一个状态必须依赖于前一个状态得到,因此必须得有从j * A[i]~m容量的背包!

当然还有一点不可忽视,在多重背包中,假设我们有1000个同种物品,那么根据二进制优化,我们最多取到2^9=512,因为2 ^ 10等于1024,会出现多取的情况,所以当二进制的最后一个数取不到物品数时,可以在二进制数后面再加上一个数字,用来补全缺少的物品数量。

这里插一句,硬币问题的数据更新完全是因为原有的金额需要向前面不定行索取值而造成的,2个硬币凑成6,现在手里的是五分的,意味着我需要把6分硬币-5分硬币剩下的那个1分硬币放入前面的硬币盒中,所以我的方法数量不变,还是1种。这是笔者临时想到的,读者不用管。

二进制优化的原理及证明

以下证明只需要是多重背包。完全背包不存在取不满,有盈余的情况。

  1. 证明一:为什么数字n能被拆分成为2^0 、2^1 、、、、、、2^n-1 ,n-2^k-1+1?
    请看下图证明
    在这里插入图片描述
    2.为什么拆成二进制数字之后能够通过组合遍历所有<=n的数字?
    在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值