一文彻底搞懂01背包算法

0-1 背包问题:给定 n 种物品和一个容量为 C 的背包,物品 i 的重量是 wi w i ,其价值为 vi v i

问:应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?

假设一个函数B是求解总价值的函数,有两个因变量n与C ;则我们的优化目标就变为

max.F(n,C,x).x0,1 m a x . F ( n , C , x ) . x ∈ 0 , 1

展开公式其实就是:
F(n,C,x)=x1v1+x2v2+....+xnvn F ( n , C , x ) = x 1 ∗ v 1 + x 2 ∗ v 2 + . . . . + x n ∗ v n
std.x1w1+x2w2+...+xnwnC s t d . x 1 ∗ w 1 + x 2 ∗ w 2 + . . . + x n ∗ w n ≤ C
xi0,1 x i ∈ 0 , 1

x的取值范围为0或者1,代表着这个物品我们可以选择拿或者不拿,我们想要找出这样的01组合如: 111001 ( 1 , 1 , 1 , 0 , 0 , 1 ) 使得 F(n,C,x) F ( n , C , x ) 最大。
我们假设一个函数
B(n,C)=max.F(n,c,x) B ( n , C ) = m a x . F ( n , c , x )
也就是说B函数是一个能够自动组合x的取值使得 F(n,c,x) F ( n , c , x ) 达到最大。
再次理解这个 B(n,c) B ( n , c ) 这个函数的意义:从n个物品里面选物品,容量为C,能达到的最大价值。如何求解这个函数呢?我们可以细分目标,试想一下,如果想要在5个商品里面选择,得到最大总价值,那么肯定得先求得在4个物品里面选择,得到最大价值后,然后考虑第五个物品要不要放进去?放进去会不会超过容量限制,会不会得到一个最大价值。我们就得到了一个函数。
B(n,c)=B(n1,c); B ( n , c ) = B ( n − 1 , c ) ; 没 有 多 余 的 空 间 去 放 置 最 后 一 个 物 品
B(n,c)=max{B(n1,c),B(n1,cwn)+vn}; B ( n , c ) = m a x { B ( n − 1 , c ) , B ( n − 1 , c − w n ) + v n } ; 如 果 有 多 余 的 空 间 去 放 置 , 则 考 虑 是 否 要 放 置

这里存在一个困扰:如果有多余空间去放置的话,那么放置肯定比不放的价值大啊,因为我多放入一个物品了啊,也就是说选择不放的函数 B(n1,x) B ( n − 1 , x ) 肯定小与放置的函数 B(n1,cw)+vn B ( n − 1 , c − w ) + v n ,说道这里你就犯了一个惯性错误,认为 B(n1,x) B ( n − 1 , x ) B(n1,cw) B ( n − 1 , c − w ) 所对应的 F(n,c,x) F ( n , c , x ) 中的 x={x1,x2,.....xn} x = { x 1 , x 2 , . . . . . x n } 组合相同,在理解这个函数的意义:从n个物品里面选物品,容量为C,能达到的最大价值。这个两个函数组合不一定相同,为容量C约束条件变了。就会变为从n-1个物品里面选,容量为C与从n-1个物品里面选,容量为c-w且加上v,两者哪个大。彻底理解了函数B之后,我相信没有什么能够阻挡你学习这个算法了。
例: w={1,2},v={1,2},c=2 w = { 1 , 2 } , v = { 1 , 2 } , c = 2
解:B(2,2)为最大价值,如果我们拿最后物品w=2,v=2,因为w=2=c,所以我们可以选择拿或者不拿
1、拿:如果确定拿走最后一个物品,则B(2,2)=B(2-1,2-2)+2=B(1,0)+2
2、不拿:如果确定不拿走最后一个物品,则B(2,2)=B(1,2);因为最后一个物品我选择不拿,所以情景肯定变为从1个物品里面选,容量为2,是否达到最大值,因此等式左右两边相等。
然后比较 B(1,0)+2 B ( 1 , 0 ) + 2 B(1,2) B ( 1 , 2 ) 哪个大,很明显,对于B(1,0),已经没有容量去放置下一个物品,就相当于从0个物品里面选 B(1,0)=B(0,0)=0B(1,0)+2=2 B ( 1 , 0 ) = B ( 0 , 0 ) = 0 , B ( 1 , 0 ) + 2 = 2 则.求解 B(1,2) B ( 1 , 2 ) ,代表着我只能去选择(第一件:2=1,v=1)的我要不要去拿,肯定得拿啊,再不拿就没东西可以拿了,结果为0,拿了话结果价值就为1
B(2,2)=max{B(1,0)+2,B(1,0)+2}=max{2,1}=2 B ( 2 , 2 ) = m a x { B ( 1 , 0 ) + 2 , B ( 1 , 0 ) + 2 } = m a x { 2 , 1 } = 2
这里写图片描述
显然这是个递归求解的过程,实现递归过程,并测试一下

capicaty=[0,1,2]
value=[0,1,2]

def  best_value(i,j):
    if i==0:
        return 0
    if j-capicaty[i]<0:
        return best_value(i-1,j)
    else:
        return max(best_value(i-1,j),best_value(i-1,j-capicaty[i])+value[i])

 print(best_value(2,2))


 result:2  

递归结果正确,但是如果数据量比较大的话,程序肯定会变的缓慢,递归会使得程序效率变差这个原因我就不解释了,但是如果我要求B(2,2)是不是要计算,B(1,2),B(0,2),如果要求B(1,2)是不是得求一下B(0,2),最后才能返回结果,我们是不是重复计算了B(1,2)与B(0,2)。如果要是能把每个计算的结果保存下来,下次就不必重复计算了,可以的,我们把递归转化为递推。
声明一个矩阵maxvalue[N][C],专门用来存放B函数的缓存结果,利用行列进行索引值,行代表有几个物品,列代表容量,这样的话如果要求B(2,2),就去索引第二个物品,容量为2 即maxvalue[2][2],找到这个值就ok了

maxvalue=[[0 for i in range(3)] for j in range(3)]
for k in range(1,3):
    for c in range(1,3):
        if c-capicaty[k]>=0:
            maxvalue[k][c]=max(maxvalue[k-1][c],maxvalue[k-1][c-capicaty[k]]+value[k])
        else:
            maxvalue[k][c]=maxvalue[k-1][c]

这里写图片描述
B(2,2)那么我们去索引,maxvalue[2][2]同样得到2的结果,这样的话这个算法你就掌握了,可以去到动态规划的世界玩耍一番,好好感悟一下,晦涩难懂的理论知识,状态,状态转移方程,把目标划分为子目标,分而治之,你会发现你看得懂了,完美。

  • 15
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值