经典算法之—背包问题

一.完全背包问题

问题描述:有一个背包容量为M,一堆物品其重量表示为W={w(1),w(2),…},物品相应的价值V={v(1),v(2),…},现在要求将物品中的一部分或全部,放入背包。要求:装入物品的总价值最高;同时满足装入物品总重量不超过M;对单个物品而言,状态可为:装入背包、不装入背包、一部分装入背包。

假设装入的物品为从w(i)到w(j),则有:


对于完全背包而言,我们采用的常见算法为:贪心算法。贪心规则为:每次放入的物品必须满足单位重量价值最高,即v(i)/w(i)最大

example:

第一步:计算单位重量价值:

第二步:放物品,按照单位重量价值来说6>5>4,因此放入顺序为w(1)、w(2)、w(3)。

显然w(1)完全放入后,背包未满;因此完全放入w(2)后,背包仍未满;但是此时若继续全部放入w(3),显然无法全部放入,只能放入三分之二的w(3)。


二.0/1背包问题

相对于完全背包问题而言,0/1背包问题多了一条规则:

物品放入规则,要么全部放入,要么不放入;不能部分放入!!

这个看似把问题简单化的规则,实际上让问题变得更加复杂。仔细想想就知道先前的贪心算法在这里失效了。因为前面的贪心规则无法保证单位重量价值最大物品一定被取到。

对于0/1背包问题,常采用的算法是:动态规划。之前的一篇关于动态规划详细讲解的文章:http://blog.csdn.net/zk_j1994/article/details/53700782

显然对于动态规划而言,最核心的事情就是寻找“状态转移函数”!!!

在寻找状态转移函数之前,说说下面这个装包过程最注意的一点就是,我们是尝试性装包,也就是说,一边往里面放,但受容量限制,还有可能拿走背包里面之前的物品,以满足不超重!!!寻找状态转移函数

(1)假设我们正在将物品向背包里面瞎装一通,此时恰好装到第i个物品。

(2)假设我们运气足够好,前面装了的i-1个恰好是局部最优解。换句话说,不可能找到其他i-1个物品放进背包能拥有更大的效益值。

(3)由于是0/1背包问题,此时我们必须要决定第i个物品到底放不放入背包?在这里,放入不放入最直观的解释就是:如果放入物品,整个效益值提高,就决定放入;反之,则不放入这里要注意了,因为我们是既往里面装,又可能往外面拿的过程。受容量限制,可能你把第i个放进去了,同时也需要从里面拿一个出来。!自然分两种情况讨论;

(4)当第i个物品不放入背包时,设设总的效益值为C[i-1,j],其中i-1表示背包中已经放入了i-1个物品,j表示背包的容量。(这里的i,j主要是为了编程的方便,因为是通过循环的方式逐渐增大i,j来选择物品是否放入的,可参考图1。)

(5)当第i个物品放入背包,放入背包之后,总的效益值就应该为C[i-1,j-w(i)]+v(i),其中i表示背包中已经放入了i个物品,j-w(i)表示背包剩余的容量。

(6)比较C[i-1,j]和C[i-1,j-w(i)]+v(i)的大小,来决定是否放入。如果C[i-1,j]<C[i,j-w(i)]+v(i),放入;否则不放入。

因此状态转移方程为:

example:

计算情况如图1:

 

图1

代码实现:


############################
#动态规划之0/1背包问题
#by:zkj
#2016年12月18日
############################
from numpy import zeros

def pack(weight,value,capacity):
    itemCnt = len(weight)                           #物品数量
    totalValue = zeros([itemCnt+1,capacity+1],int)  #存放历史最优解的数组 
    for i in range(0,itemCnt+1):                    ##状态转移函数实现
        for j in range(0,capacity+1):
            if (i == 0) or (j == 0):
                totalValue[i][j] = 0
            elif j < weight[i-1]:
                totalValue[i][j] = totalValue[i-1][j]
            else:
                totalValue[i][j] = max(totalValue[i-1][j],totalValue[i-1][j-weight[i-1]] + value[i-1])        
    return totalValue
 
def printResult(totalValue,weight,value,capacity):
    itemCnt = len(weight)
    print("背包能装的最大效益值:\n",max(totalValue[itemCnt]))
    print("装入背包的物品有:")
    i = itemCnt;j = capacity
    while i >= 0:
        if totalValue[i][j] == totalValue[i-1][j-weight[i-1]] + value[i-1]:
            print(i,end = "")   #结尾不换行
            j = j-weight[i-1]
        i -=1
    return
                      
weight = (5,3,7,4)  #物品重量
value = (3,2,9,5)   #物品价值
maxCapacity = 15    #背包容量
stateMat = pack(weight,value,maxCapacity)
printResult(stateMat,weight,value,maxCapacity)


  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值