动态规划(Dynamic Programming,DP)是解决背包问题的一种有效方法。背包问题有多种变体,其中最经典的是0/1背包问题。下面以0/1背包问题为例,详细说明动态规划解决该问题的具体步骤。
0/1背包问题描述
给定n件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。每件物品只能选择一次(要么选,要么不选),问如何选择物品放入背包,使得总价值最大。
动态规划解决步骤
1. 定义状态
定义一个二维数组dp[i][j]
,表示前i件物品恰好放入容量为j的背包时的最大价值。
2. 状态转移方程
对于每个物品i(1 ≤ i ≤ n)和每个容量j(0 ≤ j ≤ V),有两种选择:
- 不选第i件物品,则
dp[i][j] = dp[i-1][j]
。 - 选第i件物品,则
dp[i][j] = dp[i-1][j-w[i]] + v[i]
(前提是j ≥ w[i])。
因此,状态转移方程为: [ dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]) ]
3. 初始化
dp[0][j] = 0
,表示没有物品时,无论背包容量多大,最大价值都是0。dp[i][0] = 0
,表示背包容量为0时,无论有多少物品,最大价值都是0。
4. 填充DP表
按照状态转移方程,逐行逐列填充DP表。
5. 找出最优解
最终,dp[n][V]
就是最大价值。如果需要找出具体选择了哪些物品,可以回溯DP表。
具体示例
假设有4件物品,重量分别为[2, 3, 4, 5],价值分别为[3, 4, 5, 6],背包容量为5。
1. 定义状态
定义一个5×6的二维数组dp
(多一行一列用于初始化)。
2. 初始化
n = 4
V = 5
w = [2, 3, 4, 5]
v = [3, 4, 5, 6]
dp = [[0] * (V + 1) for _ in range(n + 1)]
3. 填充DP表
for i in range(1, n + 1):
for j in range(V + 1):
if j >= w[i-1]:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1])
else:
dp[i][j] = dp[i-1][j]
4. 输出DP表
for row in dp:
print(row)
输出:
[0, 0, 0, 0, 0, 0]
[0, 0, 3, 3, 3, 3]
[0, 0, 3, 4, 4, 7]
[0, 0, 3, 4, 5, 7]
[0, 0, 3, 4, 5, 7]
5. 找出最优解
最大价值为dp[4][5] = 7
。
6. 回溯找出选择的物品
selected_items = []
j = V
for i in range(n, 0, -1):
if dp[i][j] != dp[i-1][j]:
selected_items.append(i)
j -= w[i-1]
print("选择的物品索引:", selected_items)
输出:
选择的物品索引: [2, 1]
表示选择了第2件和第1件物品。
总结
动态规划解决0/1背包问题的核心在于定义合适的状态和状态转移方程,通过逐步填充DP表来找到最优解。这种方法不仅适用于0/1背包问题,还可以扩展到其他背包问题的变体,如完全背包问题、多重背包问题等。
发表回复