动态规划入门
把重叠子问题与最优子结构转化为状态转移。代码继续用 JavaScript。
1. 什么是动态规划
动态规划(DP):把大问题拆成子问题,保存子问题答案,避免重复计算,再拼出原问题答案。
适合问:
- 最优值(最大 / 最小)
- 方案数
- 可行性(能否凑成)
不适合硬套 DP:必须列出所有路径细节且状态爆炸、或根本无重叠子问题——这时更像纯回溯 / 搜索。
2. 重叠子问题与最优子结构
| 性质 | 含义 |
|---|---|
| 重叠子问题 | 不同大问题会反复算到同一子问题(如 fib(n-2)) |
| 最优子结构 | 原问题最优解可由子问题最优解推出 |
// 朴素递归:大量重复计算 → 指数时间
function fibNaive(n) {
if (n <= 1) return n
return fibNaive(n - 1) + fibNaive(n - 2)
}
fib(5) 会多次算 fib(3)、fib(2)——这就是重叠。DP / 记忆化把每个 n 只算一次。
没有最优子结构时(局部最优拼不出全局最优,又必须枚举组合),不要假装能转移。
3. 状态定义与转移方程
做题四步:
- 状态是什么(
dp[i]/dp[i][j]表示啥) - 转移怎么走(依赖哪些更小状态)
- 初始值(边界)
- 答案在哪(
dp[n]还是max(dp))
以爬楼梯为例:一次 1 或 2 阶,到 n 阶有多少种走法。
- 状态:
dp[i]= 爬到第 i 阶的方法数 - 转移:
dp[i] = dp[i-1] + dp[i-2] - 初始:
dp[1]=1, dp[2]=2 - 答案:
dp[n]
状态定义错了,后面全错;优先想「我要求的答案能不能成为某个 dp[...]」。