o-1背包问题迭代_背包问题(DP)

e403617614354e08304d815edb21b4d8.png

回顾

再来回顾一下背包问题背包问题是一个典型的组合问题,目标是让我们在容量为

的背包中尽可能装价值越高的物品,其中每件物品都对应自己的重量
和价值
。前面我们通过
暴力求解的方式将所有可能的组合一一列举出来,然后找到价值最高的组合。但是这样做的效率非常低, 时间复杂度达到了指数级的
,因此它不能成为一种我们可以接受的算法。

b23f05cdb2d53f20469b7e380016d6aa.png

如用动态规划来解决背包问题,事情就会变得简单得多。这里我们用两种方式来实现:自底向上bottom-up)和自顶向下top-down)。两者的区别可以这样来理解,假设我们需要用一些零件来拼接一件工艺品,自底向上就好比一名技术精湛的工人,虽然他不知道最后这些零件要拼出一件什么东西,但他深知这些零件的构造,熟悉拼接过程中的顺序,因此如果他一直重复拼接的过程就能最终拼出一件工艺品,因此自底向上用到的最多的方法是迭代;而自顶向下则好似一名工程师,他有一张该工艺品的设计图纸,懂得每一部分的具体功能,并且细分至零件,最后根据这张图纸将它们拼接起来,因此自底向上用到的最多的方法是递归。有同学就会问了,这和分治有什么区别?答案是当然有,工程师很聪明,因为他从来没有接触过零件的组装,因此每当有一部分零件组装好了之后,它都会拿笔记录下来组装的过程下次组装相同的零件的时候就会大大缩短所需要的时间。下面我们就来看看这两种方式是如何在背包问题中实现的吧!

70fef0e636d538a373b39d6114a3e260.png

自底向上

我们先来考虑一下背包问题的子问题。假设物品数为

,背包容量为
。子问题可以首先分为如何挑选这些物品,使得组合的价值最大,同时保证总重量小于等于
,其中
;当然,在挑选物品时,我们也可以
只考虑前
个物品
),因此,子问题可以被进一步细分为如何
挑选前
个物品,使得组合的价值最大,同时保证总重量小于等于

再来说说子问题之间的关系,我们用

来表示任意一个子问题,对于第
个物品,我们有两种可能,选或不选。
如果不选,我们就从前
件物品中找最大的组合,总重量不超过
。如果选了,那么还是从前
件物品中找最大的组合,只是总重量变为了不超过
,如果
,我们就从前
件物品中选择。
因此我们从这两种情况中取较大者作为
的最优解,表示为:

这里我们规定 S(0, x) 和
都等于
,这样我们就用一张
的二维表来记录每一个子问题的解,并用
补全

e1fac1eed8b7b7d40aff092dd7e140c3.png

我们的实例为:

,
,
;
,
;
,
;
,
。 从最简单的子问题
开始,显然
,所以

494136f2296cf6468e5e603b6211788a.png

随后我们增大

的值,直到
,得出

a17cbe43dd7c0eb2c6c5bdb7021b6904.png

第一行剩下的列我们根据关系式

填满。

91ff05475d4a32aa4869c934f5418d55.png

同理,第二行也是从

开始,逐渐增加
的值,直到
时我们得到

81f2001d0616ef15fc5639b894cf4c19.png

剩下的列同样也是通过关系式

求得。

ed874756f3366ff44cba0431a7612322.png

下面的行和列以此类推,直到把整张表填满,最后原问题的最优解为

2af5be8f000d0d1da63eaf1beed10e58.png

像这样,先从最简单的子问题入手,将子问题的解用一张表存储起来,然后后面复杂的子问题是通过前面子问题的解得出的,我们把这种方法叫做自底向上。

def 

由于需要用到

的表格,以及计算表格中的每一项,所以自底向上解决背包问题的时间和空间复杂度都为

自顶向下

自顶向下法又叫做备忘录法,它是基于递归来实现的。首先我们还是需要建一个

的表,并且表中所有项均初始化为
,表示该项还没有被计算过,并用
补全

0ab8d002ee1edfb9f29064091f7c3d8a.png

跟自底向上的方法不同的是,自顶向下着眼于原问题的解然后通过递归的方式搜索子问题的解,如果子问题没有解(表中对应项为

),则继续向下搜索直至遇到递归出口(
),每次子问题从递归出口中得到了求解,我们就将它们记录在表中的相应位置。如果子问题已经存在解,我们就直接用现有的解,以此避免子问题的重复求解,因此这里我们着眼于

5849ef49b0be7271c33d512fd8aadc7a.png

然后我们递归地向下搜索,这里即是从

中取较大者。

de95aa3a55e35b1b52f5b13b33784317.png

对应表格的值都为
,所以我们还要
继续向下搜索,一旦遇到递归出口,我们就返回

e74eba4c6749e374821a84b3fa2b8513.png

返回后,在表中记录下相应的最优解,以便后面的子问题使用,最后得到原问题的解。

2a8b805fcf237ac6957d03c14c5a0737.png

这样就完成了自顶向下的全部过程,下面就是用代码来实现它啦~

def 

同样,自顶向下解决背包问题的时间和空间复杂度也为

。但我们可以看到在记录子问题的解的表格中只有那些不为
的项才是真正被计算的部分,所以自顶向下的优势是计算的次数会比自底向上要少,但缺点是递归会造成更多的内存空间的使用。

本节全部代码

← 关于钱的两个经典问题 | 算法与复杂度​zhuanlan.zhihu.com
0ba86b9b9e8eb7bd7c8dce593a76fbf7.png
→ 弗洛伊德算法 | 算法与复杂度​zhuanlan.zhihu.com
0ba86b9b9e8eb7bd7c8dce593a76fbf7.png
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值