动态规划(Dynamic Programming, DP) 是算法中解决最优化问题的核心方法之一。其核心思想是将原问题分解为多个重叠子问题,并通过记忆化存储避免重复计算。
本文从基础概念到经典题型进行系统分类与解析,涵盖从入门到进阶的所有常见问题。
状态定义:明确 dp[i] 或 dp[i][j] 的含义
示例:
dp[i]表示以nums[i]结尾的最长递增子序列长度
状态转移方程:找到子问题之间的关系
示例:
dp[i] = max(dp[j] + 1),其中j < i且nums[j] < nums[i]
初始化:设定边界条件
示例:
dp[0] = 1
遍历顺序:根据依赖关系确定顺序
示例:一维DP常从左到右,二维DP需双重循环
dp[i] 表示爬到第 i 阶的方法数dp[i] = dp[i-1] + dp[i-2]dp[0] = 1, dp[1] = 1dp[i] 表示偷到第 i 家的最大金额dp[i] = max(dp[i-1], dp[i-2] + nums[i])dp[i] 表示以 nums[i] 结尾的 LIS 长度dp[i] = max(dp[j] + 1),其中 j < i 且 nums[j] < nums[i]dp[i] 表示以 nums[i] 结尾的最大子数组和dp[i] = max(nums[i], dp[i-1] + nums[i])dp[i][j] 表示前 i 个物品能否凑出重量 jdp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]](target + sum(nums)) / 2 的背包问题dp[i] 表示凑出金额 i 的最小硬币数dp[i] = min(dp[i], dp[i-coin] + 1)dp[i][j] 表示子串 s[i..j] 是否为回文dp[i][j] = (s[i] == s[j]) && (j-i <= 2 || dp[i+1][j-1])dp[i][j] 表示戳破区间 (i,j) 内气球的最大得分dp[i][j] = max(dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j])dp[node][0/1] 表示偷/不偷当前节点的最大金额dp[node][1] = val + dp[left][0] + dp[right][0]dp[node][0] = max(dp[left][0], dp[left][1]) + max(dp[right][0], dp[right][1])dp[i][0]:第 i 天不持有股票的最大利润dp[i][1]:第 i 天持有股票的最大利润dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])| 类型 | 经典题目 |
|---|---|
| 线性DP | 70. 爬楼梯、198. 打家劫舍 |
| 序列问题 | 300. LIS、1143. LCS |
| 背包问题 | 416. 分割等和子集、322. 零钱兑换 |
| 区间DP | 5. 最长回文子串、312. 戳气球 |
| 树形DP | 337. 打家劫舍 III |
| 状态机DP | 121. 买卖股票 I、309. 含冷冻期 |
动态规划的本质是“用空间换时间”,通过拆解问题、记录中间结果来避免重复计算。
掌握路径建议:
📌 推荐练习顺序:线性DP → 序列问题 → 背包问题 → 区间DP → 树形DP → 状态机DP


本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!