背包问题

背包问题

参考博客及文章:

1.https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html
2.https://www.cnblogs.com/yulinfeng/p/7113588.html
3.https://www.jianshu.com/p/a0aa94ef8ab2

问题描述:有n个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?

eg:number=4,capacity=8

i1234
w(体积)2345
v(价值)3456

1.思想

动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。

分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。

2. 过程

1.抽象化问题

Vi:第i个物品的价值,Wi:表示第i个物品的重量

Xi:表示是否选择第i个物品(Xi=1或0,表示选或者不选第i个物品)

V(i,j):当前背包容量为j,选择前i个物品的最佳组合

2.建立模型

max(\sum_{i=1}^n{X_i*V_i})

3.约束条件

max(\sum_{i=1}^n{X_i*W_i})<capacity

4.寻找递推关系


V(i,j)可由V(i-1,j)和V(i-1,j-w[i])两个子问题递推而来:

  • 包的容量无法装下第j件商品,则不装,此时V(i,j)=V(i-1,j)
  • 包的容量可以装下该商品,但装了该商品未必满足最优解,此时在装与不装之间选择最优解。
    即V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i) }
    其中V(i-1,j)表示不装,V(i-1,j-w(i))+v(i) 表示装了第i个商品,背包容量减少w(i)但价值增加了v(i)。

    由此可以得出递推关系式:
  1. j<w(i) V(i,j)=V(i-1,j)
  2. j>=w(i) V(i,j)=max{ V(i-1,j),V(i-1,j-w(i))+v(i) }

5.填表

首先初始化边界条件,V(0,j)=V(i,0)=0。然后一行一行的填表:

1) 如,i=1,j=1,w(1)=2,v(1)=3,有j<w(1),故V(1,1)=V(1-1,1)=0;

2) 又如i=1,j=2,w(1)=2,v(1)=3,有j=w(1),故V(1,2)=max{ V(1-1,2),V(1-1,2-w(1))+v(1) }=max{0,0+3}=3;

3) 如此下去,填到最后一个,i=4,j=8,w(4)=5,v(4)=6,有j>w(4),故V(4,8)=max{ V(4-1,8),V(4-1,8-w(4))+v(4) }=max{9,4+6}=10;最后结果如下表

i/j012345678
0000000000
1003333333
2003447777
3003457899
40034578910

6.求解背包组成

表格填完,最优解即是V(number,capacity)=V(4,8)=10,但还不知道解由哪些商品组成,故要根据最优解回溯找出解的组成,根据填表的原理可以有如下的寻解方式:

  1. V(i,j)=V(i-1,j)时,说明没有选择第i个商品,则回到V(i-1,j);
  2. V(i,j)=V(i-1,j-w(i))+v(i)实时,说明装了第i个商品,该商品是最优解组成的一部分,随后我们得回到装该商品之前,即回到V(i-1,j-w(i));
  3. 一直遍历到i=0结束为止,所有解的组成都会找到。
  • 如上例子最优解为V(4,8)=10,而V(4,8)!=V(3,8)却有V(4,8)=V(3,8-w(4))+v(4)=V(3,3)+6=4+6=10,所以第4件商品被选中,并且回到V(3,8-w(4))=V(3,3);
  • 有V(3,3)=V(2,3)=4,所以第3件商品没被选择,回到V(2,3);
  • 而V(2,3)!=V(1,3)却有V(2,3)=V(1,3-w(2))+v(2)=V(1,0)+4=0+4=4,所以第2件商品被选中,并且回到V(1,3-w(2))=V(1,0);
  • 有V(1,0)=V(0,0)=0,所以第1件商品没被选择;

7.代码

#背包问题
global item
item=[0 for i in range(4)]                 #标记背包内是否装了第i件物品

def findWhat(V,i,j,v,w):                   #查找背包内物品价值最大时放置了哪几件物品
    if i>=1:
        if V[i][j]==V[i-1][j]:
            item[i-1]=0
            findWhat(V,i-1,j,v,w)
        elif ( j-w[i-1]>=0 and V[i][j]==V[i-1][j-w[i-1]]+v[i-1] ) :
            item[i-1]=1
            findWhat(V,i-1,j-w[i-1],v,w)

#动态规划求背包价值最大值
# w[i]:第i件物品的重量,v[i]:第i件物品的价值
# V[x][y]:背包容量为y,选择前x件物品的最优解
# n:物品个数,c:背包总容量
def findMax(w,v,n,c):
    i=1
    j=1
    V=[[0 for i in range(c+1)] for i in range(n+1)]
    for i in range(1,n+1):                   #还未扫描到最后一件物品
        for j in range(1,c+1):               #包还有剩余空间
            if j<w[i-1]:                     #包装不下第i件物品
                V[i][j]=V[i-1][j]
            else:
                if (V[i-1][j]>(V[i-1][j-w[i-1]]+v[i-1])): #不装第i件物品价值更大
                    V[i][j]=V[i-1][j]
                else:
                    V[i][j]=V[i-1][j-w[i-1]]+v[i-1]
    return V

if __name__=="__main__":
    n=4               #物品个数
    c=8               #背包容积
    w=[2,3,4,5]       #每件物品的重量
    v=[3,4,5,6]       #每件物品的价值
    V=findMax(w,v,n,c)
    print(V[4][8])
    findWhat(V,n,c,v,w)
    for i in range(n):
        if (item[i]==1):
            print("选择了第",i+1,"件商品")

注意

因为V(i,j)是由V(i-1,j)和V(i-1,j-w[i])两个子问题递推而来,所以对于V,循环时i和j应从1开始,而对于w[]和v[],i是从0开始,编写代码时应当注意

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值