2025-11-17-关于动态规划(背包问题为例)
Last Update:
Page View: loading...
动态规划介绍
动态规划(Dynamic Programming,简称 DP)是一种算法设计范式,用于解决具有重叠子问题和最优子结构性质的优化问题。它通过将原问题分解为若干子问题,先解决子问题并保存结果(通常使用表格或数组),从而避免重复计算,最终构建出原问题的解。
动态规划的核心思想源于数学中的贝尔曼最优性原理:最优策略的子策略总是最优的。与分治法不同,动态规划适用于子问题重叠的情况,能将指数级时间复杂度优化至多项式级。


动态规划的适用条件
- 最优子结构:原问题的最优解可由子问题的最优解组合得出。
- 重叠子问题:子问题在递归求解中会被多次计算。
实现方式
- 自底向上(表格填充):使用循环从小型子问题开始填充 DP 表格,直至原问题(常见于背包问题)。
- 自顶向下(记忆化递归):递归求解并使用备忘录存储已计算子问题结果。
以 0/1 背包问题为例
0/1 背包问题(0/1 Knapsack Problem)是动态规划的经典应用:给定 n 个物品,每个物品有重量 w_i 和价值 v_i,以及一个容量为 W 的背包。每个物品只能选择取或不取(0/1),求背包中物品的最大总价值。


问题分析
- 子问题:考虑前 i 个物品和容量为 j 的背包的最大价值。
- 状态定义:
dp[i][j]表示前 i 个物品在容量 j 下的最大价值。 - 状态转移方程:
- 不取第 i 个物品:
dp[i][j] = dp[i-1][j] - 取第 i 个物品(若 j ≥ w_i):
dp[i][j] = dp[i-1][j - w_i] + v_i dp[i][j] = max(以上两种情况)
- 不取第 i 个物品:
- 边界:
dp[0][j] = 0(无物品),dp[i][0] = 0(容量为 0)
示例
假设物品:重量[1, 3, 4],价值[15, 20, 30],背包容量 W = 4。
DP 表格填充过程如下所示:
初始状态(i=0,无物品)
1 | |
考虑第 1 个物品(重量 1,价值 15)
- 对于 w < 1:无法放入,只能取上一行值(0)。
- 对于 w ≥ 1:max(不放入: 0, 放入: 0 + 15) = 15。
1 | |
考虑第 2 个物品(重量 3,价值 20)
- 对于每个 w:
- 如果 w < 3:无法放入,取上一行值。
- 如果 w ≥ 3:max(不放入:
dp[1][w], 放入:dp[1][w-3] + 20)。
计算示例:
- w=3:max(15, 15 + 20) = 35
- w=4:max(15, 15 + 20) = 35
1 | |
考虑第 3 个物品(重量 4,价值 30)
- 对于每个 w:
- 如果 w < 4:无法放入,取上一行值。
- 如果 w ≥ 4:max(不放入:
dp[2][w], 放入:dp[2][w-4] + 30)。
计算示例:
- w=4:max(35, 0 + 30) = 35(不放入更好)
1 | |
最终结果:dp[3][4] = 35(选择第 1 和第 2 个物品:重量 1+3=4,价值 15+20=35)。
一维 DP 优化过程简要文本模拟
一维数组从后向前更新(避免覆盖):
初始 dp: [0, 0, 0, 0, 0](容量 0 到 4)
第 1 个物品后: [0, 15, 15, 15, 15]
第 2 个物品后: [0, 15, 15, 35, 35]
第 3 个物品后: [0, 15, 15, 35, 35]
复杂度分析
- 时间复杂度:O(n × capacity)
- 空间复杂度:二维 O(n × capacity),一维 O(capacity)
伪代码(自底向上)
1 | |
时间复杂度 O(nW),空间复杂度 O(nW)(可优化至 O(W) 使用一维数组)
动态规划通过系统地记录子问题解,避免了暴力递归的冗余计算,在组合优化、序列问题等领域广泛应用。理解背包问题有助于掌握 DP 的状态设计与转移核心。
0/1 背包问题的 TypeScript 实现详解
0/1 背包问题(0/1 Knapsack Problem)是动态规划的经典示例。给定 n 个物品,每个物品具有重量 weights[i] 和价值 values[i],以及一个背包容量 capacity。每个物品只能选择放入或不放入(不可重复),目标是求背包内物品的最大总价值。
动态规划状态定义与转移
- 状态:
dp[i][w]表示考虑前 i 个物品(索引 0 到 i-1),背包容量为 w 时的最大价值。 - 转移方程:
- 如果不放入第 i 个物品:
dp[i][w] = dp[i-1][w] - 如果放入第 i 个物品(前提
w ≥ weights[i-1]):dp[i][w] = dp[i-1][w - weights[i-1]] + values[i-1] dp[i][w] = Math.max(不放入, 放入)
- 如果不放入第 i 个物品:
- 初始条件:
dp[0][w] = 0(无物品),dp[i][0] = 0(容量为 0)。 - 结果:
dp[n][capacity]
TypeScript 实现
以下提供两种实现:标准二维 DP 和空间优化的一维 DP(推荐,后者空间复杂度 O(capacity))
1. 二维 DP 实现
1 | |
2. 一维 DP 实现(空间优化,使用滚动数组)
1 | |
示例
1 | |
- 物品数量 n = 3
- 重量 weights = [1, 3, 4]
- 价值 values = [15, 20, 30]
- 背包容量 capacity = 4