背包问题:背包问题是线性 DP 问题中一类经典而又特殊的模型。背包问题可以描述为:给定一组物品,每种物品都有自己的重量、价格以及数量。再给定一个最多能装重量为 W 的背包。现在选择将一些物品放入背包中,请问在总重量不超过背包载重上限的情况下,能装入背包的最大价值总和是多少?
0-1背包问题
0-1 背包问题: 有 n 件物品和有一个最多能装重量为 W 的背包。第 i 件物品的重量为 weight [i], 价值为 value [i], 每件物品有且只有 1 件。请问在总重量不超过背包载重上限的情况下, 能装入背包的最大价值是多少?
每种物品有且仅有1件,可以选择不放入背包,也可以选择放入背包。
定义状态dp[i][w]表示为:前i件物品放入一个最多能装重量为w的背包中,可以获得的最大价值。
如果w < weight[i-1], dp[i][w] = dp[i-1][w]
考虑第i-1件物品,如果剩余的最大载重小于该物品重量,则无法放入。
如果w >= weight[i-1], max{dp[i-1][w], dp[i-1][w-weight[i-1]]+value[i-1]}
考虑第i-1件物品,如果剩余的最大载重大于该物品重量,则可以选择放入或者不放入。
class Solution:
# 思路 1:动态规划 + 二维基本思路
def zeroOnePackMethod1(self, weight: [int], value: [int], W: int):
size = len(weight)
dp = [[0 for _ in range(W + 1)] for _ in range(size+1)
for i in range(1, size + 1):
for w in range(W + 1):
if w < weight[i - 1]:
dp[i][w] = dp[i-1][w]
else:
dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i-1]]+value[i-1])
return dp[size][W]
- 时间复杂度:O(n×W),其中 n 为物品数量,W 为背包的载重上限。
- 空间复杂度:O(n×W)。
动态规划 + 滚动数组优化
在状态转移的过程中,我们只用到了当前行(第 i 行)的 dp[i][w]以及上一行(第 i−1行)的 dp[i−1][w]、dp[i−1][w−weight[i−1]]。
我们需要按照「从 W∼0逆序的方式」倒推 dp[w]。
因为我们需要i-1状态的dp[w]的值,但是我们现在只有一维dp,只需要逆序倒推就可以保证获取的dp[w-weight[i-1]]依旧是i-1状态下的值。
class Solution:
# 思路 2:动态规划 + 滚动数组优化
def zeroOnePackMethod2(self, weight: [int], value: [int], W: int):
size = len(weight)
dp = [0 for _ in range(W + 1)]
for i in range(1, size + 1):
for w in range(W, weight[i - 1] - 1, -1):
dp[w] = max(dp[w], dp[w - weight[i - 1]] + value[i - 1])
return dp[W]
- 时间复杂度:O(n×W),其中 n 为物品数量,W 为背包的载重上限。
- 空间复杂度:O(W)。