跳转至

线性动态规划⚓︎

数字三角形问题⚓︎

例题:数字三角形

数字三角形下标

本题DP数组及数字三角形的下标最好从1开始。

数字三角形动态规划问题:

  • 状态表示f(i,j)
    • 集合:所有从起点走到点 (i,j) 的路径;
    • 属性:集合中所有路径对应的数字之和的最大值。
  • 状态计算:将集合 f(i,j) 分为两类:
    • 来自左上方的路径: f(i1,j1)+a[i][j]
    • 来自右上方的路径: f(i1,j)+a[i][j]

动态规划问题是时间复杂度一般为“状态数量”乘以“每个状态转移需要的计算量”,本题时间复杂度为 O(n2)

最长上升子序列问题⚓︎

例题:最长上升子序列保存最长上升子序列

最长上升子序列动态规划问题:

  • 状态表示f[i]
    • 集合:所有以第 i 个数为结尾的数值上升子序列的集合;
    • 属性:集合每个上升子序列的长度的最大值。
  • 状态计算:按倒数第二个数是哪个来划分集合,要么序列里只有一个数( 0 ),要么倒数第二个数的下标可能为 1,2,,n1,这些类不一定都存在。

对于最后两个数分别为 aj,ai 的上升子序列( aj<ai ),满足其长度为 f[j]+1,则 f[i]=max(f[j]+1),aj<ai; j=0,1,,i1

时间复杂度为 O(n2)

例题:最长上升子序列2

当数据范围较大时,应该寻找一种时间复杂度更低的方式。

扩展解法可把时间复杂度降到 O(nlogn)

最长公共子序列问题⚓︎

例题:最长公共子序列

最长公共子序列(不是字串)动态规划问题:

  • 状态表示f(i,j)
    • 集合:所有由第一个序列的前 i 个字母和第二个序列的前 j 个字母构成的公共子序列,也就是所有在第一个序列的前 i 个字母中出现,且在第二个序列的前 j 个字母中出现的子序列;
    • 属性:所有这些子序列长度的最大值。
  • 状态计算:以a[i]b[j]是否包含在子序列中来划分集合(共四种情况):
    • “情况 00 ” (不选a[i],不选b[j]): f(i1,j1),一般代码中可以不写这种情况,因为 f(i1,j)f(i,j1) 已经包含了这种情形;
    • “情况 01 ” (不选a[i],选b[j]):注意 f(i1,j) 不一定包含b[j](即“情况 01 ”为 f(i1,j) 的子集,而不是相等),但为了求最大值,可以用 f(i1,j) 来替换,即使集合划分出现重复也没事(因为求的是最大值,只要不漏掉元素就行);
    • “情况 10 ” (选a[i],不选b[j]):为了求最大值,可以用 f(i,j1) 来替换;
    • “情况 11 ” (选a[i],选b[j]): f(i1,j1)+1

时间复杂度为 O(n2)

注:以上第二种情况(“情况 01 ”)指的是a[i]不出现在子序列中,b[j]一定出现在子序列中, f(i1,j) 严格包含这种情况。为什么?

第二种状态(“情况 01 ”)的意思是:a[i]一定不在子序列中,且最后一个字母一定是b[j];而 f(i1,j) 的意思是,a的前i - 1b的前j中出现公共子序列,所以是第二种状态的条件更强。

  • 为什么有重复?因为第二种状态更强, f(i1,j) 条件弱一点,所以用 f(i1,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]为以上三种情况的最小值,时间复杂度为 O(n2)

例题:编辑距离