动态规划
01 / 共享的机制
一张表格逐格填充自己 —— 每一格都由 常数个更早的格子组合而来。递推关系因子模式而异, 但底层机制 —— 状态、转移、填表顺序 —— 是共通的。
02 / 一句话本质
每个下标处于一个小而固定的状态集合中的某一个, 并有明确的状态间转移规则。把状态机沿输入展开 —— DP 表中每个状态一行,每个位置一列。
题目含冷冻期的股票最大利润价格[1, 2, 3, 0, 2]
d0
$1
d1
$2
d2
$3
d3
$0
d4
$2
持有
·
·
·
·
·
卖出
·
·
·
·
·
休息
·
·
·
·
·
每天三种状态:held(持有股票)、sold(刚卖出 —— 明天进入冷冻期)、rest(不持有,可自由买入)。逐日填表。
步
0 / 14
正在填
—
来源
—
最大利润
—
0 / 14
03 / 模式骨架
# 状态机 DP —— 跟踪每个状态随时间的变化dp[state][0] ← 每个状态的基线for i in 1..n−1:for each state:dp[state][i] ← 对所有合法的源状态 state' 取 max/min(dp[state'][i−1] + cost(state'→state, input[i]))return 在终止状态上取 max dp[state][n−1]# 冷冻期:状态 = held / sold / rest# held[i] = max(held[i−1], rest[i−1] − price[i])# sold[i] = held[i−1] + price[i]# rest[i] = max(rest[i−1], sold[i−1])
04 / 什么时候用这个模式
"有限模式"
每个位置的选择取决于当前所处的模式 —— 持有还是不持有、连续还是断开、 剩余可交易次数。模式就是状态。
"转移约束"
某些动作从某些状态出发是非法的 —— 卖出后必须先休息再买、不能连续跳两个、必须交替。 非法转移编码了题目的规则;合法转移则成为递推式。
"答案按终止状态求"
最终答案是位置
n − 1 上各终止状态之间的最优。 常常是“不持有”—— 但有时题目要求“必须以状态 X 结束”,那就只读一行。"状态数为小常数"
通常是 2–5 个状态。当状态数随输入增长(例如交易次数
k), DP 会多出一个维度,复杂度变为 O(N · k)。05 / 常见坑
状态缺失或重叠。
如果你的状态没有干净地划分所有可能的位置,转移就会爆炸,递推式也会出现歧义。 自检:任意时刻的每个合法位置都能被恰好分配到一个状态上吗?做不到就先精化状态集再写代码。
各状态的基线不对称。
每个状态都需要自己的第 0 天初值,而且很少全为零。对冷冻期问题,
held[0] = -price[0](今天买入),而 sold[0] = rest[0] = 0(尚未交易)。基线处的差一会一路扩散到之后的每个格子。状态之间存在隐藏耦合。
填
dp[B][i] 时读 dp[A][i-1] 是没问题的;但若读 dp[A][i](同一列)就会产生次序依赖:在第 i 天内, 必须先填 A 再填 B。务必先画出转移图, 再让内层循环顺序与之相符。06 / 去 LeetCode 上练习
中等13
01Decode Ways— LC 91→02Best Time to Buy and Sell Stock II— LC 122→03House Robber— LC 198→04House Robber II— LC 213→05Paint House— LC 256→06Paint Fence— LC 276→07Best Time to Buy and Sell Stock with Cooldown— LC 309→08Best Time to Buy and Sell Stock with Transaction Fee— LC 714→09Delete and Earn— LC 740→10Online Stock Span— LC 901→11Knight Dialer— LC 935→12Prison Cells After N Days— LC 957→13Number of Ways to Select Buildings— LC 2222→
困难9
01Best Time to Buy and Sell Stock III— LC 123→02Best Time to Buy and Sell Stock IV— LC 188→03Paint House II— LC 265→04Student Attendance Record II— LC 552→05Number of Music Playlists— LC 920→06Distinct Subsequences II— LC 940→07Handshakes That Don't Cross— LC 1259→08Number of Ways to Paint N x 3 Grid— LC 1411→09Paint House III— LC 1473→
— / 什么时候用哪个
一维线性
当下标 i 处的答案只依赖常数个更早的下标时使用。
打家劫舍 · 爬楼梯 · 最大子段和
二维网格
当状态由两个下标组成 —— 网格,或两条相互作用的序列。
最短路径 · LCS · 编辑距离
背包
当每个物品都触发一次选 / 不选决策、且有容量约束时使用。
子集和 · 找零 · 划分
区间
当状态是一个区间
(i, j)、答案由内部区间组合得出时使用。回文 · 矩阵链 · 戳气球
状态机
当每个下标可以处于若干有限状态之一、且状态之间有明确的转移规则时使用。
股票 · 冷冻期 · 双状态
树形 DP
当问题定义在树上、且每个节点的答案依赖其子节点答案时使用。
打家劫舍 III · 二叉树直径 · 最大路径和
位掩码
当状态是子集(通常
N ≤ 20),且只关心选了哪些、不关心顺序时使用。分配 · TSP · 最小覆盖团队