[动态规划系列] —— 背包DP之01背包

01背包问题

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

考虑中间状态,有i个物品,有j个容量,该状态的最高价值为status[i][j]。该状态能够由其上一个状态转移得到,对于第i个物品,咱们能够将其丢弃或放入背包,取二者最大值。python

丢弃:status[i][j] = status[i-1][j]数组

放入:status[i][j] = value[i] + status[i-1][j-volume[i]]code

这里的(i,j)状态,是第i个物品已经作出选择的结果,他须要上一个状态即选择第i-1个物品后的结果转移而来。j - volume[i]能够理解为,选择物品i后的容量是j,那么未选择以前为j - volume[i]。leetcode

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-1][j-volume[i-1]])
            else: status[i][j] = status[i-1][j]
    return status[n][v]

对于矩阵status,其状态只在i-1和i两行之间转移,咱们能够使用滚动数组也就是只用两行来不断地更新,维护上一步和当前步的状态,以达到下降空间复杂度的目的。get

这里使用状态压缩,只用一行来记录状态,将二维DP降到一维DP。将上面的代码稍做改动,能够获得下面的错误代码,这里只是单纯的把i状态删掉了。虽然是错误的代码,但咱们须要它来辅助理解。it

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]])
            else: status[j] = status[j]
    return status[v]

该段代码的问题在于,对于status[j]的计算须要依赖于status[j-volume[i]]的结果,在二维中,status[j]和status[j-volume[i]]是分别处于不一样行的,也就是i与i-1两行,可是在一维中,它们都在同一行。io

而且j值是向右增加的,也就是说在计算status[j]时,status[j-volume[i]]的值早已被更新为第i行的状态了,而不是上一步的状态。因此咱们须要让j值向左增加。获得以下代码。ast

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

这段代码已是正确的了,可是还不够完美,观察后不难发现status[j] = status[j]这一步是彻底没有必要存在的,所以咱们能够控制j的最小值,保证j - volume[i-1] >= 0成立。获得最终代码以下。class

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

之因此该问题被称做01背包,其缘由在于对于每同样物品只能使用一次。在咱们遇到的题目中,每每是01背包问题的变体,咱们须要学会如何将题目转换为经典的01背包问题。co

相关题目:分割等和子集一和零最后一块石头的重量 II

相关文章
相关标签/搜索