01背包递归、记忆搜索、动态规划求解,空间优化

01背包问题

01背包问题相信大家都很熟悉,问题描述如下:

有一个背包,它的容量为C,现在有 n n n种不同的物品,编号为 0... n − 1 0...n-1 0...n1,其中每一件物品的重量为 w ( i ) w(i) w(i),价值为 v ( i ) v(i) v(i),问可以向这个背包中放那些物品,使得在不超过背包容量的基础上,物品的总价值最大。

我们假设求解问题状态为:
F ( n , c ) F(n,c) F(nc)考虑将 n n n个物品放进容量为 C C C的背包,使得价值最大。

问题状态转移为一下两种情况:

  1. F ( i , c ) = F ( i − 1 , c ) F(i,c)=F(i-1,c) F(i,c)=F(i1,c)
  2. F ( i , c ) = v ( i ) + F ( i − 1 , c − w ( i ) ) F(i,c)=v(i)+F(i-1,c-w(i)) F(i,c)=v(i)+F(i1,cw(i))
    情况1中,我们假定当前物品i直接丢弃不放进背包
    情况2中,我们假定当前物品i直接放进背包

在求解 F ( i , c ) F(i,c) F(i,c) 的过程中只存在这两种情况,下面我们先使用递归自顶向下求解:

class Solution:
    def knapsack01(self, weight, value, c):
        return self.getMax(weight, value, c, len(weight) - 1)

    def getMax(self, w, v, c, index):
        if index < 0 or c <= 0:
            return 0

        # 第一种情况,不考虑当前index指向的物品,直接看index+1物品的价值和重量
        res = self.getMax(w, v, c, index - 1)
        # 第二种情况,直接将当前index指向物品放入背包
        if c >= w[index]:
            res = max([res, v[index] + self.getMax(w, v, c - w[index], index - 1)])
        return res

在递归过程中,我们发现存在很多重复计算的子问题,而这些子问题是存在于物品价值v和背包容量之间相互对应的。下面我们使用基于记忆搜索的方法,记录子问题的求解记录。

class Solution:
    def knapsack01_memo(self, weight, value, c):
        self.memo = [[-1 for _ in range(c + 1)] for _ in range(len(weight))]
        return self.getMax_memo(weight, value, c, len(weight) - 1)

    def getMax_memo(self, w, v, c, index):
        if index < 0 or c <= 0:
            return 0
        if self.memo[index][c] is not -1:
            return self.memo[index][c]
        # 第一种情况,不考虑当前index指向的物品,直接看index+1物品的价值和重量
        res = self.getMax_memo(w, v, c, index - 1)
        # 第二种情况,直接将当前index指向物品放入背包
        if c >= w[index]:
            res = max([res, v[index] + self.getMax_memo(w, v, c - w[index], index - 1)])
        self.memo[index][c] = res
        return res

通过进一步分析,我们使用dp自顶向上的方法来解决该问题,初始化二维数组 m e m o memo memo大小为 w . s i z e w.size w.size行, c c c列, m e m o memo memo存放过程如下:
在这里插入图片描述
m e m o memo memo列值代表当前背包容量 c ( j ) c(j) c(j),行代表待存放物品个数 w ( i ) w(i) w(i),当i=0时, m e m o [ i ] [ j ] = c ( j ) > = w ( 0 ) ? v [ i ] : 0 memo[i][j]=c(j)>=w(0) ? v[i]:0 memo[i][j]=c(j)>=w(0)?v[i]:0 ,即判断当前背包容量是否可以存下 w ( i ) w(i) w(i)。接着 i > = 1 i>=1 i>=1时,我们取当前 i i i至少可以取到的值以及 v [ t i m e ] + m e m o [ t i m e − 1 ] [ c − w [ t i m e ] ] v[time] + memo[time - 1][c - w[time]] v[time]+memo[time1][cw[time]]中的最大值。

class Solution:
    # dp求解背包问题
    def knapsack01_dp(self, w, v, c):
        memo = [[-1 for _ in range(c + 1)] for _ in range(len(v))]
        for i in range(c + 1):
            memo[0][i] = v[0] if i >= w[0] else 0
        for time in range(1, len(v)):
            for c in range(c + 1):
                # 情况一 直接计算当前容量至少存放的最大价值
                memo[time][c] = memo[time - 1][c]
                if c >= w[time]:
                    memo[time][c] = max([memo[time][c], v[time] + memo[time - 1][c - w[time]]])
        return memo[-1][-1]

在上述过程中,我们发现 m e m o memo memo遍历时,每次只使用了最后两行数组,所以我们在空间上对其进行优化,只创建一个两行的数组,当循环次数等于偶数时更新第2行,次数为奇数时更新第一行。

class Solution:
        def knapsack01_ADM(self, w, v, c):
        memo = [[0 for _ in range(c + 1)] for _ in range(2)]
        for i in range(c + 1):
            memo[0][i] = v[0] if i >= w[0] else 0
        for time in range(1, len(w)):
            for cap in range(c + 1):
                i, j = (1, 0) if time & 1 else (0, 1)
                memo[j][cap] = memo[i][cap]
                if cap >= w[time]:
                    memo[j][cap] = max([memo[j][cap], v[time] + memo[i][c - w[time]]])
        return memo[-1][-1]
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值