
算法进阶:从“套公式”到“识破”动态规划(DP)
在学习动态规划时,最大的痛苦莫过于:看题解像看小说一样顺畅,自己写时却连第一步“定义状态”都迈不出去。 本文将带你跳出死记硬背的怪圈,通过剖析核心概念与实战逻辑,帮你建立起真正的 DP 直觉。
DP 识别雷达:什么时候该想到它?
在没有标签提示的情况下,如果题目满足以下特征,你的“DP 雷达”就该响了:
- 目标关键词
- 求最值:最大利润(股票)、最小步数(路径)、最长序列(LIS)。
- 求方案总数:有多少种走法、有多少种拼凑组合。
- 求可行性:是否能凑出目标金额、是否能拆分字符串。
- 决策结构
如果题目要求你做出一系列决策,且当前的决策会依赖之前的状态,但不影响之后还没做的决策,这通常就是 DP。
核心基因:判定 DP 的标准
- 重叠子问题 (Overlapping Subproblems):
- 逻辑:在解决大问题时,会反复遇到相同的小问题。
- 策略:既然算过一次,就用空间换时间(存入数组),下次直接查表。
- 最优子结构 (Optimal Substructure):
- 逻辑:大问题的最优解可以由小问题的最优解推导而来。
- 类比:如果你想找北京到上海的最短路,它一定包含北京到南京的最短路。
专业术语深度解码
- 状态 (State) —— “游戏的存档点”
状态是某个阶段的“进度存档”,它必须包含解决后续问题所需要的所有必要信息。
- 打家劫舍: 是“偷到第 家时的最高金额”。
- 股票买卖: 不仅记录了天数,还记录了“是否持股”这个关键维度。
- 无后效性 (No-aftereffect) —— “存档的纯粹性”
这是 DP 最抽象也最核心的概念。它要求:一旦当前状态确定,未来的演变只取决于当前值,与你是如何到达这个状态的历史路径无关。
💡 形象类比:RPG 游戏的存档
- 【有后效性的存档】(坏存档):存档里只记了你的“当前位置”。读档后你发现:因为你之前没拿钥匙,现在进不去门。这意味着“位置”这个状态不够用,历史(拿没拿钥匙)还在干扰未来。
- 【无后效性的存档】(好存档):存档里记了“位置 + 钥匙情况”。不管你是怎么拿到钥匙的,只要在这个位置且有钥匙,后序通关难度完全一样。历史被彻底浓缩进了状态里。
🔍 深度案例:LeetCode 174《地下城游戏》
在这道题中,骑士血量必须全程 。
-
正向推导(产生后效性):
如果你记录“当前剩余血量”。 -
路径 A:剩 100 血,但之前最惨时剩 1(门槛高)。
-
路径 B:剩 50 血,但之前最惨时剩 40(门槛低)。
此时你无法判定 A 还是 B 更好,因为现在的优劣受未来(是否有大怪)和过去(起伏情况)双重牵扯。 -
逆向推导(消除后效性):
定义 为:“从当前格出发直到终点,骑士进入该格前至少需要的血量”。
当你算出这个值(比如需要 5 点血),那么不管你之前经历了什么,只要进门时有 5 点血,你就一定能通关。历史被切断了,未来变得可预期,这就是无后效性。
- 状态转移方程 (State Transition Equation)
它是 DP 的灵魂,描述了状态如何“演变”。
- 公式: d p [ i ] = max ( d p [ i − 1 ] , nums [ i ] + d p [ i − 2 ] ) dp[i] = \max(dp[i-1], \text{nums}[i] + dp[i-2]) dp[i]=max(dp[i−1],nums[i]+dp[i−2])
- 本质:这是在每一个决策点做出“最优取舍”。
实战套路:攻克 DP 的四步法
- 定义状态:问自己,“为了继续做题,我最少需要记住哪些信息?”(确定维度)。
- 推导方程:思考“最后一个动作是什么?”(选或不选?左还是下?)。
- 确定初始化:填好最简单的边界(如 ),这是启动循环的“第一推”。
- 确定遍历顺序:通常是自底向上。确保你在算 时,它需要的子问题(如 )已经算好了。
总结
- 如果你发现状态不够用:说明存在后效性。尝试增加维度(给状态打补丁)或者改变推导方向(正难则反)。
- 空间换时间:不要害怕开数组。如果空间太大,再考虑使用“滚动数组”优化。

923

被折叠的 条评论
为什么被折叠?



