线性动态规划⚓︎
数字三角形问题⚓︎
例题:数字三角形
本题DP数组及数字三角形的下标最好从1
开始。
数字三角形动态规划问题:
- 状态表示:
:- 集合:所有从起点走到点
的路径; - 属性:集合中所有路径对应的数字之和的最大值。
- 集合:所有从起点走到点
- 状态计算:将集合
分为两类:- 来自左上方的路径:
; - 来自右上方的路径:
。
- 来自左上方的路径:
动态规划问题是时间复杂度一般为“状态数量”乘以“每个状态转移需要的计算量”,本题时间复杂度为
最长上升子序列问题⚓︎
最长上升子序列动态规划问题:
- 状态表示:
:- 集合:所有以第
个数为结尾的数值上升子序列的集合; - 属性:集合每个上升子序列的长度的最大值。
- 集合:所有以第
- 状态计算:按倒数第二个数是哪个来划分集合,要么序列里只有一个数(
),要么倒数第二个数的下标可能为 ,这些类不一定都存在。
对于最后两个数分别为
时间复杂度为
例题:最长上升子序列2
当数据范围较大时,应该寻找一种时间复杂度更低的方式。
扩展解法可把时间复杂度降到
最长公共子序列问题⚓︎
例题:最长公共子序列
最长公共子序列(不是字串)动态规划问题:
- 状态表示:
:- 集合:所有由第一个序列的前
个字母和第二个序列的前 个字母构成的公共子序列,也就是所有在第一个序列的前 个字母中出现,且在第二个序列的前 个字母中出现的子序列; - 属性:所有这些子序列长度的最大值。
- 集合:所有由第一个序列的前
- 状态计算:以
a[i]
、b[j]
是否包含在子序列中来划分集合(共四种情况):- “情况
” (不选a[i]
,不选b[j]
): ,一般代码中可以不写这种情况,因为 和 已经包含了这种情形; - “情况
” (不选a[i]
,选b[j]
):注意 不一定包含b[j]
(即“情况 ”为 的子集,而不是相等),但为了求最大值,可以用 来替换,即使集合划分出现重复也没事(因为求的是最大值,只要不漏掉元素就行); - “情况
” (选a[i]
,不选b[j]
):为了求最大值,可以用 来替换; - “情况
” (选a[i]
,选b[j]
): 。
- “情况
时间复杂度为
注:以上第二种情况(“情况 a[i]
不出现在子序列中,b[j]
一定出现在子序列中,
第二种状态(“情况 a[i]
一定不在子序列中,且最后一个字母一定是b[j]
;而 a
的前i - 1
和b
的前j
中出现公共子序列,所以是第二种状态的条件更强。
- 为什么有重复?因为第二种状态更强,
条件弱一点,所以用 表示第二种状态里面肯定会有重复的; - 为什么求最大值可以重复?如果取最大值的话,重复也就无所谓了,就算重复最大值依然是最大值。
编辑距离问题⚓︎
例题:最短编辑距离
该问题可进行如下分析:
- 状态表示:
- 集合:所有将
a[1~i]
变为b[1~j]
的操作方式; - 属性:所有操作方式的操作次数的最小值。
- 状态计算:
f[i,j]
,按如下方式划分集合: - 最后一步 删除
a[i]
:f[i - 1, j] + 1
; - 最后一步 增加
a[i] == b[j]
:f[i, j - 1] + 1
; - 最后一步 修改
a[i]
:根据a[i]
与b[j]
是否相等来分类讨论,f[i - 1, j - 1] + 1或0
。
f[i, j]
为以上三种情况的最小值,时间复杂度为
例题:编辑距离