langShiftlangShift

多维动态规划(题单顺序)

热题 100 - 多维动态规划分组(按题单顺序):62 / 64 / 5 / 1143 / 72

本页包含题单「多维动态规划」分组的全部题目,顺序与 list.json 保持一致。

多维 DP 统一“落地步骤”(点击展开)
  • 状态dp[i][j] 表示什么(一定要带上“含义 + 范围”)。
  • 转移dp[i][j] 从哪些更小状态来,为什么不会漏/重。
  • 初始化:边界(第一行/第一列/空串/长度为 0)怎么设,避免越界。
  • 遍历顺序:保证用到的状态已经算好(常见是从小到大)。
  • 优化:能否滚动数组(把二维压成一维),以及滚动时更新方向是否会污染数据。

62. 不同路径

力扣原题:62. 不同路径
Medium
数学
动态规划
组合数学

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

 

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

 

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109
最优解(二维 DP:dp[i][j]=dp[i-1][j]+dp[i][j-1],可滚动成一维)
正在加载编辑器...
最优解讲解(通俗版 + 推理过程)
  • 题意:你站在左上角,每次只能往右或往下走一步,问到右下角一共有多少种不同的走法?
  • 核心想法:走到某一个格子时,你只能从它的上面一格下来,或者从它的左边一格过来,没有别的路。所以「到达这个格子的走法数」=「到达上面那格的走法数」+「到达左边那格的走法数」。写成式子就是:dp[i][j] = dp[i-1][j] + dp[i][j-1]
  • 边界:第一行只能一直往右走,第一列只能一直往下走,所以第一行、第一列每个格子都只有 1 种走法,初始化为 1。
  • 空间优化:算每一行时只用得到「上一行」的结果,所以可以用一维数组「滚动」:dp[j] 表示当前行第 j 列有多少种走法,更新时用「上一行的 dp[j]」加上「当前行左边的 dp[j-1]」,即 dp[j] = dp[j] + dp[j-1]
类似题目(网格 DP:来自上/左)

思路相同:当前格子的值只依赖「上面」和「左边」,转移时用一维滚动即可,例如:

dp[j] = dp[j] + dp[j-1]

64. 最小路径和

力扣原题:64. 最小路径和
Medium
数组
动态规划
矩阵

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

 

示例 1:

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 200
最优解(二维 DP:dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j])
正在加载编辑器...
最优解讲解(通俗版 + 推理过程)
  • 和 62 一样,只是把“路径条数”换成“路径代价最小”。
  • grid[i][j] 的最小代价只能来自上或左的最小代价,再加上当前格子权重。
  • 一维滚动时:
    • dp[j] 表示从上方来的最小和
    • dp[j-1] 表示从左方来的最小和(因为已更新到当前行)
    • 所以 dp[j] = min(dp[j], dp[j-1]) + grid[i][j]
类似题目(网格最短路 DP:来自上/左取 min)
dp[j] = min(dp[j], dp[j-1]) + cost[i][j]

5. 最长回文子串

力扣原题:5. 最长回文子串
Medium
双指针
字符串
动态规划

给你一个字符串 s,找到 s 中最长的 回文 子串

 

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

 

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成
最优解(中心扩展:每个中心向两边扩,O(n^2) 时间 O(1) 空间)
正在加载编辑器...
最优解讲解(通俗版 + 推理过程)
  • 回文的定义:左右对称。
  • 关键推理:最长回文一定有一个“中心”
    • 奇数长度回文中心是一个字符(比如 aba 的中心是 b)
    • 偶数长度回文中心在两个字符之间(比如 abba 的中心在 bb 之间)
  • 所以我们枚举每个可能中心,然后向两边扩展,直到不再相等。
  • 为什么是 (O(n^2)):中心有 (O(n)) 个,每次扩展最坏 (O(n))。
  • 这题也可以用二维 DP 表 pal[i][j],同样是 (O(n^2)),中心扩展更省空间、更直观。
类似题目(中心扩展模板)
for center in all:
expand(center, center)
expand(center, center+1)

1143. 最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

 

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

 

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1 和 text2 仅由小写英文字符组成。
最优解(二维 DP:dp[i][j] 表示前 i/前 j 的 LCS 长度)
正在加载编辑器...
最优解讲解(通俗版 + 推理过程)
  • 题意:从两个字符串里各挑一些字符(顺序不变、可以不连续),能凑出的最长相同串有多长?这就是「最长公共子序列」。
  • 想法dp[i][j] 表示:只看 text1 的前 i 个、text2 的前 j 个,这段里最长公共子序列的长度。
  • 怎么算?看「最后一个字符」:
    • 一样:这两个字符可以一起算进公共子序列,长度就是「前面那段的答案 + 1」,即 dp[i][j] = dp[i-1][j-1] + 1
    • 不一样:这两个里至少有一个不能配对。那就看两种选择谁更大:不用 text1 的最后一个(相当于 dp[i-1][j]),或不用 text2 的最后一个(相当于 dp[i][j-1]),取较大值:dp[i][j] = max(dp[i-1][j], dp[i][j-1])
类似题目(字符串二维 DP:末尾相同走对角,不同取上/左 max)

套路一样:看两串「末尾」是否相同,相同就对角 +1,不同就从上/左取最大:

if (a[i - 1] === b[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1
else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])

72. 编辑距离

力扣原题:72. 编辑距离
Medium
字符串
动态规划

给你两个单词 word1 和 word2请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

 

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

 

提示:

  • 0 <= word1.length, word2.length <= 500
  • word1word2 由小写英文字母组成
最优解(二维 DP:插入/删除/替换 三选一)
正在加载编辑器...
最优解讲解(通俗版 + 推理过程)
  • 编辑距离定义:把 word1 变成 word2 的最少操作数(插入、删除、替换)。
  • 状态定义dp[i][j] 表示把 word1 前 i 个字符变成 word2 前 j 个字符的最小操作数。
  • 初始条件
    • dp[i][0]=i:变成空串只能删 i 次
    • dp[0][j]=j:从空串变成长度 j 只能插 j 次
  • 转移推理
    • 如果末尾字符相同,不需要操作:dp[i][j]=dp[i-1][j-1]
    • 否则末尾要对齐,有三种方式:
      1. 删除 word1 末尾:dp[i-1][j] + 1
      2. 插入 word2 末尾:dp[i][j-1] + 1
      3. 替换 word1 末尾为 word2 末尾:dp[i-1][j-1] + 1 取最小。
类似题目(编辑类 DP:三操作取 min)
if same: dp[i][j]=dp[i-1][j-1]
else dp[i][j]=1+min(del, ins, rep)