数据结构与算法: 动态规划篇1 之0-1背包问题


11

(一) 0-1 背包问题

对于一组不同重量、不可分割的物品,我们需要选择一些装入背包,
在满足背包最大重量限制的前提下,背包中物品总重量的最大值是多少呢?
回溯的解决方法是穷举搜索所有可能的装法,然后找出满足条件的最大值。
不过,回溯算法的复杂度比较高,是指数级别的, 复杂度为O(2^n)。

递归树
递归树中的每个节点表示一种状态,我们用(i, cw)来表示。
其中,i 表示将要决策第几个物品是否装入背包,cw 表示当前背包中物品的总重量。比如,(2,2)表示我们将要决策第 2 个物品是否装入背包,在决策前,背包中物品的总重量是 2。
递归树中,你应该能会发现,有些子问题的求解是重复的,比如图中 f(2, 2) 和 f(3,4) 都被重复计算了两次。
记录已经计算好的 f(i, cw),当再次计算到重复的 f(i, cw) 的时候,可以直接从备忘录中取出来用,就不用再递归计算了,这样就可以避免冗余计算。

动态规划: 优化前 (二维数组)

把问题分解为多个阶段,每个阶段对应一个决策。我们记录每一个阶段可达的状态集合??>(去掉重复的),然后通过当前阶段的状态集合,来推导下一个阶段的状态集合,动态地往> 前推进。这也是动态规划这个名字的由来

我们把整个求解过程分为 n 个阶段,每个阶段会决策一个物品是否放到背包中。
每个物品决策(放入或者不放入背包)完之后,背包中的物品的重量会有多种情况,也就是说,会达到多种不同的状态,对应到递归树中,就是有很多不同的节点。

我们把每一层重复的状态(节点)合并,只记录不同的状态,然后基于上一层的状态集合,来推导下一层的状态集合。我们可以通过合并每一层重复的状态,这样就保证每一层不同状态的个数都不会超过 w 个(w 表示背包的承载重量),也就是例子中的 9。于是,我们就成功避免了每层状态个数的指数级增长。
用一个二维数组 states[n][w+1],来记录每层可以达到的不同状态。
下面先放代码,然后再讲解

# 时间复杂度: O(n*w)
# weight: 物品重量, n:物品个数  w:背包可承载重量
# 需要额外申请一个 n 乘以 w+1 的二维数组,对空间的消耗比较多。
def knapsack(weight,w):
    n = len(weight)
    states = [[0] * (w + 1) for i in range(n)] # 设置一个n行w+1列的二维数组,数组中值都为0(也即是False)
    states[0][0] = True  # 第一行的数据要特殊处理
    if weight[0] <= w: # 第一个物品的重量
        states[0][weight[0]] = True

    for i in range(1,n): # 动态规划状态转移    剩下的物品
        for j in range(0,w+1): # 不把第i个物品放入背包
            if states[i-1][j] == True:
                states[i][j] = states[i-1][j]
        for j in range(0,w-weight[i]+1): # 把第i个物品放入背包
            if states
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值