动态规划问题总结【学习中】
子串:连续的
子序列:不需要连续
DP总览
标准问法
计数(多少种路径...)
- Li114,多少种路径
- 多少种方法选出k个数,使得和是sum
最值(最大利润、最小数量、最长子串...)
- Li669,硬币的最少个数
- Li076,最长上升子序列
存在性(可行性、能否)
- Li116,能否跳到最后一块石头
问题特点
- 可以变成子问题(最优子结构)
- 子问题之间存在重复计算
解题步骤
-
确定状态
- 考虑最后一步(最后一个物品,最后一个路径,最后一个部分)
- 变成解决子问题了(规模更小的问题)
- 状态就是计算大问题时必要的子问题的信息
-
确定状态转移方程
- ❗ dp[i] 和 f(i) 不完全一样
P2O_2e 14
,Li515
- dp[i]的计算顺序(从大到小还是从小到达)有影响
0-1背包
- ❗ dp[i] 和 f(i) 不完全一样
-
处理初始条件和边界情况
- 初始条件:开始的几项(用不了转移方程求解的就是初始条件)
- 边界情况:
- 数组下标越界时
- 情况不存在时的dp值(比如股票问题的负无穷)
注意计算dp的顺序
- 原则就是计算需要的数据都是之前计算的结果,以及别覆盖还要用的数据(压缩空间时注意)
- 一般都是从小到大,背包问题的空间优化必须反向计算
空间优化
- 使用滚动数组, 根据状态转移方程中需要的最旧的数据确定大小
- 先写出不压缩的,然后用
dp[i % n]
代替dp[i]是最方便, 最好理解的解决办法, 其中n是压缩后的数组大小。用个变量名cur = i % n更容易修改 - 一般是逐行扫描, 滚动数组长度是列数的倍数。 如果列多行少时, 可以考虑逐列扫描让滚动数组长度是列数的倍数, 进一步优化空间
坐标型20%
问题特点
- 条件: 一般给矩阵
- 状态定义 : 状态dp[i]的含义是以a[i]结尾的序列的性质
- 转移方程 : /
- 返回 : 最后返回dp[n - 1]
刷题记录
Leet | Lintcode | 简介 | TO | SO | 收获 |
---|---|---|---|---|---|
L62 | Li114 | 不同路径 | |||
L63 | Li115 | 不同路径,网格中有障碍 | |||
L64 | Li110 | 路径上数字和的最小值 | |||
Lv361 | Li553 | 炸弹袭击 | 1. 分解成4个方向,分别记录状态; 2. 记录状态时,敌人和墙也可以炸 |
序列型20%
问题特点
-
条件 : 一般给一维数组
-
状态定义 : 状态dp[i]的含义是前i个元素的序列的性质
-
转移方程 : 这种状态定义下对应的转移方程,❗for循环是
i <= n
( 经常写成i < n
,导致输出0) -
返回 : 最后返回dp[n]
刷题记录
Leet | Lint | 简介 | TO | SO | 收获 |
---|---|---|---|---|---|
Lv256 | Li515 | 刷房子 | |||
Lv265 | Li556 | 刷房子,k种颜色 | O(n*k) |
||
L198 | Li392 | 打家劫舍 | |||
L213 | Li534 | 打家劫舍II 环形街区 | |||
L121 | Li149 | 买卖股票的最佳时机(一次交易) | |||
L122 | Li150 | 买卖股票的最佳时机II(多次交易) | |||
L123 | Li151 | 买卖股票的最佳时机III(2次交易) | |||
L188_L面63 | Li393 | 买卖股票的最佳时机IIII(k次交易) | |||
L338 | Li664 | 一个范围内所有的数,各自的数位中1的个数 |
划分型20%
问题特点
- 条件 : 一般字符串或数组
- 状态定义 : 状态dp[i]用坐标型的含义
- 转移方程 : dp[i]时枚举最后一段的可能的起点
- 返回:dp[n - 1]
刷题记录
Leet | Lint | 简介 | TO | SO | 收获 |
---|---|---|---|---|---|
L91 | Li512 | 编码解析方法 | |||
L279 | Li513 | 最少平方数 | O(n^3/2) | 最优解 🔳四平方数和定理 | |
L132 | Li108 | 分割回文串 | O(n^2) | 中轴方法判断回文串 | |
/ | Li437🐱💻 | 抄书 | 最优解 🔳贪心二分 求和Trick |
||
L55 | Li116 | 跳跃石头 | O(n^2) | 最优解 🔳贪心 |
子序列型5%
LIS:Longest Increasing Subsequence
问题特点
- 条件 : 字符串或数组
- 状态定义 : 状态dp[i]用坐标型的含义
- 转移方程 : /
- 返回 : ❗dp[0] ~ dp[n - 1]中的最大值
刷题记录
Leet | Lint | 简介 | 时间复杂度 | 空间复杂度 | 收获 |
---|---|---|---|---|---|
L300 | Li076 | 最长上升子序列 | O(N^2) |
🔳patience game解法O(N*logN) |
|
L53 | Li41 | 最大连续子序列和 | |||
L354 | Li602 | 信封套娃(和最长上升子序列解法一样) | 🔳O(N*logN) |
||
/ | Li397 | 最长单向(上升或下降)连续子序列(最长单向子串) | 上升下降分别求 |
区间型15%
子问题需要去头去尾
问题特点
- 条件 : 字符串或数组
- 状态定义 :
dp[i][j]
, 字符i到j的区间的性质 - 转移方程 : 循环变量是区间的长度
- 返回 :
dp[0][n - 1]
刷题记录
Leet | Lint | 简介 | 时间复杂度 | 空间复杂度 | 收获 |
---|---|---|---|---|---|
L516 | Li667 | 最长回文子序列 | O(N^2) |
||
L877 | LiV396 | 只能从两端取数,谁的和最大谁赢 | |||
L87 | Li430 | 扰乱字符串🐱💻 | |||
L312 | Li168 | 戳气球(消去型->区间型)🐱💻 |
背包型10%
载重W的背包,N件待选物品,物品属性:重量w,价值v
问题特点
- 条件 : 数组
- 状态定义 : 状态dp[i]用序列型的含义,重量一定要记录在状态中
- 转移方程:最后一步是最后一个物品 是否进入背包(0-1) / 是哪一个(完全)
- 返回 :/
分类
问法分类
- 可行型
- 最值型
- 要求正好装满
- 不要求正好装满
- 计数型
要求分类
-
0-1背包,列表中的物品只有一个
-
完全背包,列表中的物品有任意个
刷题记录
Leet | Lint | 简介 | 时间复杂度 | 空间复杂度 | 收获 |
---|---|---|---|---|---|
/ | Li92 | 最值型0-1背包,最大重量 | |||
Li125 | 最值型0-1背包,最大价值 | ||||
/ | Li563 | 计数型0-1背包,装满的方案数 | |||
Li562 | 计数型完全背包,装满的方案数(不同顺序算一种方案) | ||||
/ | Li564 | 计数型完全背包,和为指定值的组合数(不同顺序算不同方案) | 描述更复杂解决却更简单(股票问题也是) | ||
L332 | Li669 | 最值型完全背包,换硬币的最少个数 | |||
LiV440 | 最值型完全背包,最大价值 |
博弈型5%
问题特点
从第一步开始考虑而不是最后一步
弄清楚必胜态,必败态
刷题记录
Leet | Lint | 简介 | 时间复杂度 | 空间复杂度 | 收获 |
---|---|---|---|---|---|
/ | Li394 | 谁最后没东西拿谁输 | O(N) |
||
L877 | LiV396 | 只能从两端取数,谁的和最大谁赢 |
双序列型
问题特点
- 条件 : 字符串或数组
- 状态定义 :
dp[i][j]
, 第一个序列的前i个字符和第二个序列的前j个字符的性质 - 转移方程 :最后一步,考虑怎么砍尾巴
- 返回 :
dp[m][n]
刷题记录
Leet | Lint | 简介 | 时间复杂度 | 空间复杂度 | 收获 |
---|---|---|---|---|---|
L1143 | Li77 | 最长公共子序列LCS | |||
L97 | Li29 | 交错字符串Iterleaving String | |||
L72 | Li119 | 最小编辑距离Edit Distance |