动态规划(Dynamic Programming,DP)是解决背包问题的一种有效方法。背包问题有多种变体,如0/1背包问题、完全背包问题、多重背包问题等。这里以经典的0/1背包问题为例,详细解释动态规划解决该问题的关键步骤。
0/1背包问题描述
给定一组物品,每个物品有一个重量和一个价值,以及一个背包的最大承载重量。目标是选择一些物品放入背包,使得总价值最大且总重量不超过背包的最大承载重量。
关键步骤
-
定义状态:
- 设
dp[i][j]
表示前i
个物品在背包容量为j
时的最大价值。
- 设
-
状态转移方程:
- 对于每个物品
i
(从1到n)和每个容量j
(从0到C),有两种选择:- 不选第
i
个物品:dp[i][j] = dp[i-1][j]
- 选第
i
个物品(前提是j
要大于等于第i
个物品的重量w[i]
):dp[i][j] = dp[i-1][j-w[i]] + v[i]
- 不选第
- 综合上述两种情况,状态转移方程为: [ dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]) ]
- 对于每个物品
-
初始化:
dp[0][j] = 0
,表示没有物品时,无论背包容量多大,最大价值都是0。dp[i][0] = 0
,表示背包容量为0时,无论有多少物品,最大价值都是0。
-
遍历顺序:
- 通常采用两层循环:
- 外层循环遍历物品,从1到n。
- 内层循环遍历背包容量,从0到C。
- 通常采用两层循环:
-
求解结果:
- 最终结果存储在
dp[n][C]
中,表示前n
个物品在背包容量为C
时的最大价值。
- 最终结果存储在
代码示例(Python)
def knapsack(weights, values, capacity):
n = len(weights)
创建dp表,初始化为0
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
# 填充dp表
for i in range(1, n + 1):
for j in range(0, capacity + 1):
if j >= weights[i-1]:
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][capacity]
示例输入
weights = [2, 3, 4, 5] values = [3, 4, 5, 6] capacity = 5
调用函数
max_value = knapsack(weights, values, capacity) print("最大价值:", max_value)
其他变体的关键步骤
-
完全背包问题:
- 状态转移方程变为:
dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i])
- 内层循环遍历顺序改为从0到C。
- 状态转移方程变为:
-
多重背包问题:
- 可以通过二进制拆分将多重背包问题转化为0/1背包问题,再使用类似的方法求解。
总结
动态规划解决背包问题的关键在于定义合适的状态和状态转移方程,并通过合理的遍历顺序填充DP表,最终得到问题的解。不同类型的背包问题在状态转移方程和遍历顺序上有所差异,但基本思路是一致的。
发表回复