leetcode的揹包问题

01 揹包

题目

N件物品和一个容量为V的揹包。第i件物品的费用是w[i],价值是v[i],求将哪些物品装入揹包可使价值总和最大。

题解

  1. 定义数组
dp[i][j] # 代表前i个商品,放入一个容量为j的揹包,所获得的最大价值
  1. 初始化
# 如果要求恰好放满揹包,则数组初始化为 -inf
dp = [[-inf] * (amount + 1) for _ in range(n)]
# 如果要求恰好放满揹包,则数组初始化为 0
dp = [[0] * (amount + 1) for _ in range(n)]

需要格外强调的是:第一、无论初始化是如何进行的,但是我们每次迭代商品的时候:dp[i][0]一定要等于0,因为放满容量为0的揹包,价值肯定是0。第二、动态数组的长度是(容量+1)(要考虑容量为0的情形)。

  1. 转移方程
for i in range(n+1): # 初始化工作对i==0的时候已经做了处理,所以这里可以不用再考虑。
	for j in range(w[i],v+1): # 正序
		f[i][j]=max(f[i−1][j],f[i−1][j−w[i]]+v[i])

01 揹包简化

我们观察转移方程:

f[i][j]=max(f[i−1][j],f[i−1][j−w[i]]+v[i])

发现f[i][j]只和上一行正上方上一行的左上方有关。如果我们只用一行数据正序遍历的话,我们会将上一行左上方的数据先覆盖掉。所以我们采用逆序遍历。

def package_01(size, values, weights):
    # dp[j]代表装满容量为j的揹包能产生的最大价值

    import numpy
    # 如果不要求完全装满
    dp = numpy.array([0] * (size + 1))
    # 如果要求完全装满
    # dp = [-float("inf")] * (size + 1)

    # 初始化,无论是要求装满还是不装满,初始化规则一样,只是如果要求不装满,已经是0,可以省略初始化条件
    dp[0] = 0

    for i in range(len(values)):
        for j in range(size, weights[i] - 1, -1): # 逆序
            dp[j] = max(dp[j], dp[j - weights[i]] + values[i])

    return dp[size]

完全揹包

题目

N种商品和一个容量为V的揹包。第i件物品有无数件,且费用是w[i],价值是v[i],求将哪些物品装入揹包可使价值总和最大。

题解

完全揹包和01揹包的逻辑基本类似,但是由于物品可以任意次取,所以我们每个物品取用多少次 取决于当 0<=k*w[i]<=j时,f[i−1][j−k*w[i]]+k*v[i]最大。

f[i][j]=max(f[i−1][j−k*w[i]]+k*v[i]) 0<=k*w[i]<=j

将上面的逻辑转为程序语言

k = 1
while k * weights[i] <= j:
    dp[i][j] = max(dp[i][j], dp[i - 1][j - k * weights[i]] + k * weights[i])
    k += 1

最终解法:

def package_total(size, values, weights):
    import numpy
    # 如果不要求完全装满
    dp = numpy.array([[0] * (size + 1) for _ in range(len(values))])

    # 初始化,无论是要求装满还是不装满,初始化规则一样,只是如果要求不装满,已经是0,可以省略初始化条件
    dp[0][0] = 0

    for i in range(len(values)):
        for j in range(weights[i], size + 1):
            k = 1
            while k * weights[i] <= j:
                dp[i][j] = max(dp[i][j], dp[i - 1][j - k * weights[i]] + k * weights[i])
                k += 1

    return dp[-1][-1]

完全揹包简化

def package_total(size, values, weights):
    # dp[j]代表装满容量为j的揹包能产生的最大价值

    import numpy
    # 如果不要求完全装满
    dp = numpy.array([0] * (size + 1))

    # 如果要求完全装满
    # dp = [-float("inf")] * (size + 1)

    # 初始化,无论是要求装满还是不装满,初始化规则一样,只是如果要求不装满,已经是0,可以省略初始化条件
    dp[0] = 0

    for i in range(len(values)):
        for j in range(weights[i], size + 1): # 正序
            dp[j] = max(dp[j], dp[j - weights[i]] + values[i])

    return dp[size]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章