什么是01背包问题?
01背包问题是经典的动态规划问题之一。它的基本描述是:给定一组物品,每个物品有一个重量和一个价值,在限定的总重量内,如何选择物品使得总价值最大。
问题描述
- 输入:
- 一个整数数组
weights
,表示每个物品的重量 - 一个整数数组
values
,表示每个物品的价值 - 一个整数
W
,表示背包的最大承重
- 一个整数数组
- 输出:
- 最大可能的总价值
举个例子
假设有以下物品:
物品 | 重量 | 价值 |
---|---|---|
1 | 2 | 3 |
2 | 3 | 4 |
3 | 4 | 5 |
4 | 5 | 8 |
背包最大承重 W = 5
。我们可以选择哪些物品使得总价值最大呢?
动态规划解法
状态定义
我们用一个二维数组 dp[i][j]
来表示前 i
个物品在容量为 j
的情况下能够获得的最大价值。
状态转移方程
- 如果不选第
i
个物品,那么dp[i][j] = dp[i-1][j]
- 如果选第
i
个物品,那么dp[i][j] = dp[i-1][j-weights[i]] + values[i]
综合起来,我们有:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i]] + values[i])
边界条件
当没有任何物品或者容量为0时,最大价值为0:
dp[0][j] = dp[i][0] = 0
实现代码
def knapsack(weights, values, W):
n = len(weights)
dp = [[0 for _ in range(W + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, W + 1):
if weights[i - 1] <= j:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])
else:
dp[i][j] = dp[i - 1][j]
return dp[n][W]
# 测试
weights = [2, 3, 4, 5]
values = [3, 4, 5, 8]
W = 5
print(knapsack(weights, values, W)) # 输出: 最大值为8
空间优化
上述解法用了二维数组来存储状态,但其实我们只关心上一行的数据,可以将空间复杂度从 O(nW) 优化到 O(W)。
def knapsack_optimized(weights, values, W):
n = len(weights)
dp = [0 for _ in range(W + 1)]
for i in range(n):
for j in W, weights[i] - 1, -1:
dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
return dp[W]
# 测试
weights = [2,3,4,5]
values = [3,4,5,8]
W=5
print(knapsack_optimized(weights, values, W)) # 输出: 最大值为8
小结
01背包问题虽然经典,但也非常实用。通过动态规划和空间优化,我们可以高效地解决这个问题。希望这对你有帮助。