[动态规划系列] —— 背包DP之完全背包[通俗易懂]

[动态规划系列] —— 背包DP之完全背包[通俗易懂]如果你阅读过我的上一篇文章01背包,那么完全背包问题的代码只需要在其基础之上作很小的改动。在01背包的状态压缩中我们提到,j值需要向左增长,保证能够正确的引用到上一次状态的结果。 而若是j值向右增长,那么我们引用的“上一次状态”实际上是已经被更新的当前次的状态,这一步的含义指当…

[动态规划系列]

完全背包问题

有n个物品,1个容量v的背包,第i个物品体积是volume[i],价值是value[i],问将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大,每个物品可以使用无限次。

如果你阅读过我的上一篇文章01背包,那么完全背包问题的代码只需要在其基础之上作很小的改动。在01背包的状态压缩中我们提到,j值需要向左增长,保证能够正确的引用到上一次状态的结果。

而若是j值向右增长,那么我们引用的“上一次状态”实际上是已经被更新的当前次的状态,这一步的含义指当前物品可以被重复选择,而这恰好就是完全背包问题。

def solution(n, v, volume, value):
    status = [0]*(v+1)
    for i in range(1, n+1):
        for j in range(volume[i-1], v+1): # 相比于01背包,只有这一行做了改动
            status[j] = max(status[j], value[i-1] + status[j-volume[i-1]])
    return status[v]

如果上面的解释还没有理解,继续向下看,但请确保你已经理解了01背包问题。


首先回忆01背包中status[i][j] = max(status[i-1][j], value[i-1] + status[i-1][j-volume[i-1]])的含义。

在状态转移的过程中,我们考虑前i-1件物品的状态,和是否选择第i件物品,保证了01背包问题中每个物品只能选择一次的特性。

而对于完全背包问题,每件物品可以无限使用,也就是对于第i件物品,我们可以重复选择,在这种情况下,status[i][j]可以由status[i][j-volume[i-1]]转移而来。

def solution(n, v, volume, value):
    status = [[0]*(v+1) for _ in range(n+1)]
    for i in range(1, n+1):
        for j in range(1, v+1):
            if j - volume[i-1] >= 0:
                status[i][j] = max(status[i-1][j], value[i-1] + status[i][j-volume[i-1]]) # 相比于01背包,只有这一行做了改动
            else: status[i][j] = status[i-1][j]
    return status[n][v]

同样的,我们对完全背包版本的代码作状态压缩。

def solution(n, v, volume, value):
    status = [0]*(v+1)
    for i in range(1, n+1):
        for j in range(1, v+1):
            if j - volume[i-1] >= 0:
                status[j] = max(status[j], value[i-1] + status[j-volume[i-1]]) # 相比于01背包,只有这一行做了改动
            else: status[j] = status[j]
    return status[v]

我们会发现状态压缩后,完全背包和01背包的状态转移方程一致,考虑01背包的j为何要向左增长?是因为01背包中需要尚未更新的status[j-volume[i-1]],也就是status[i-1][j-volume[i-1]]

而完全背包需要的是status[i][j-volume[i-1]],即已经更新的status[j-volume[i-1]],所以j需要向右增长,再对分支结构做简单的优化,可以得到最终版本完全背包问题的代码。

def solution(n, v, volume, value):
    status = [0]*(v+1)
    for i in range(1, n+1):
        for j in range(volume[i-1], v+1):
            status[j] = max(status[j], value[i-1] + status[j-volume[i-1]])
    return status[v]

这也就是本文最开始给出的代码。

之所以该问题被称作完全背包,其原因在于对于每一样物品可以使用无限次。在我们遇到的题目中,往往是完全背包问题的变体,我们需要学会如何将题目转换为经典的完全背包问题。

相关题目:零钱兑换 II,组合总和 Ⅳ。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13929.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注