动态规划题目汇总(持续更新)

动态规划题目汇总(持续更新)

动态规划的核心是写出问题的状态转移方程(即递推公式),有了状态转移方程,编写代码就是很简单的事了。
如何写出状态转移方程才是动态规划最关键的问题。
因此,这里只给出每个问题的状态转移方程,代码只是对数学公式的翻译。

1. 钢条切割

该问题是《算法导论》中动态规划章节的第一个例题,是一个很简单的动态规划问题。

问题描述

某公司购买长钢条,并将其切割成短钢条出售,切割工序本身无成本。
假定该公司出售一段长度为 i i i 英寸的钢条价格为 P i ( i = 1 , 2 , 3 , 4 … , n ) P_i (i=1,2,3,4…,n) Pi(i=1,2,3,4,n) ,求最佳切割方案,使总收益最大。
钢条价格表部分实例如下:

长度 i i i12345678910
价格 P i P_i Pi1589101717202430

解法

r n r_n rn 为长度为 n n n 的钢条能获得的最大收益, P i P_i Pi 为长度为 i i i 的钢条的价格。
则该问题的状态转移方程为:
r n = { 0 , n = 0 max ⁡ { P i + r n − i   ∣   i = 1 , … , n } , n > 0 r_n = \begin{cases} 0, & n = 0 \\ \max \{P_i + r_{n - i} \ | \ i = 1, \dots, n\}, & n > 0 \end{cases} rn={0,max{Pi+rni  i=1,,n},n=0n>0

2. 矩阵链乘法

该问题是《算法导论》中动态规划章节的第二个例题,难度适中。

问题描述

给定一个 n n n 个矩阵的序列(矩阵链) < A 1 , A 2 , … , A n > < A_1, A_2, \dots, A_n > <A1,A2,,An> ,我们希望计算它们的乘积 A 1 A 2 … A n A_1 A_2 \dots A_n A1A2An ,矩阵 A i A_i Ai 的大小为 p i − 1 × p i ( 1 ⩽ i ⩽ n ) p_{i - 1} \times p_i (1 \leqslant i \leqslant n) pi1×pi(1in) 。求完全括号化方案,使得乘积 A 1 A 2 … A n A_1 A_2 \dots A_n A1A2An 所需的标量乘法次数最少。

解法

m [ i , j ] m[i, j] m[i,j] 为乘积 A i A i + 1 … A j A_i A_{i + 1} \dots A_j AiAi+1Aj 所需的最小计算量, A i … j A_{i \dots j} Aij 为矩阵 A i , A i + 1 , … , A j A_i, A_{i + 1}, \dots, A_j Ai,Ai+1,,Aj 相乘得到的矩阵, A i … k A_{i \dots k} Aik A k + 1 … j A_{k + 1 \dots j} Ak+1j 相乘的代价为 p i − 1 p k p j p_{i - 1} p_k p_j pi1pkpj
则该问题的状态转移方程为:
m [ i , j ] = { 0 , i = j min ⁡ { m [ i , k ] + m [ k + 1 , j ] + p i − 1 p k p j   ∣   k = i , … , j } , i < j m[i, j] = \begin{cases} 0, & i = j \\ \min \{m[i, k] + m[k + 1, j] + p_{i - 1} p_k p_j \ | \ k = i, \dots, j \}, & i < j \end{cases} m[i,j]={0,min{m[i,k]+m[k+1,j]+pi1pkpj  k=i,,j},i=ji<j

3. 最长公共子序列

该问题是《算法导论》中动态规划章节的例题,也是《剑指 Offer II》中的第95题和 LeetCode 中的第1143题,在LeetCode中的难度为中等。
LeetCode 1143. 最长公共子序列

问题描述

一个序列的子序列是指这样一个新的序列:它是由原序列在不改变元素的相对顺序的情况下删除某些元素(也可以不删除任何元素)后组成的新序列。
最长公共子序列(Longest-Common-Subsequence, LCS)问题:给定两个序列 X m = < x 1 , x 2 , … , x m > X_m = < x_1, x_2, \dots, x_m > Xm=<x1,x2,,xm> Y n = < y 1 , y 2 , … , y n > Y_n = < y_1, y_2, \dots, y_n > Yn=<y1,y2,,yn> ,求解最长的公共子序列的长度。

解法

x i x_i xi y j y_j yj 分别为 X m X_m Xm Y n Y_n Yn 的第 i i i j j j 个元素, X i X_i Xi Y j Y_j Yj 分别为 X m X_m Xm Y n Y_n Yn 的前 i i i j j j 个元素构成的序列, c [ i , j ] c[i, j] c[i,j] X i X_i Xi Y j Y_j Yj 的最长公共子序列的长度。
则该问题的状态转移方程为:
c [ i , j ] = { 0 , i = 0  或  j = 0 c [ i − 1 , j − 1 ] + 1 , x i = y j max ⁡ { c [ i − 1 , j ] , c [ i , j − 1 ] } , x i ≠ y j c[i, j] = \begin{cases} 0, & i = 0 \ 或\ j = 0 \\ c[i - 1, j - 1] + 1, & x_i = y_j \\ \max \{c[i - 1, j], c[i, j - 1]\}, & x_i \neq y_j \end{cases} c[i,j]= 0,c[i1,j1]+1,max{c[i1,j],c[i,j1]},i=0  j=0xi=yjxi=yj

4. 无重复字符的最长子串

该问题是《剑指 Offer II》中的第16题,也是 LeetCode 中的第3题,在 LeetCode 中的难度为中等。
LeetCode 3. 无重复字符的最长子串

问题描述

给定一个字符串,找出其中不含有重复字符的最长子串的长度。

解法

c i c_i ci 为字符串中的第 i i i 个字符, d d d c i c_i ci 与前面最近的相同字符的距离, f [ i ] f[i] f[i] 为以 c i c_i ci 为结尾的无重复字符的最长子串的长度。
则该问题的状态转移方程为:
f [ i ] = { f [ i − 1 ] + 1 , c i 未出现过 d , d ⩽ f [ i − 1 ] f [ i − 1 ] + 1 , d > f [ i − 1 ] f[i] = \begin{cases} f[i - 1] + 1, & c_i 未出现过 \\ d, & d \leqslant f[i - 1] \\ f[i - 1] + 1, & d > f[i - 1] \end{cases} f[i]= f[i1]+1,d,f[i1]+1,ci未出现过df[i1]d>f[i1]

5. 最长回文子串

该问题是 LeetCode 中的第5题,在 LeetCode 中的难度为中等。
LeetCode 5. 最长回文子串

问题描述

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

解法

c i c_i ci 为字符串中的第 i i i 个字符, f [ i , j ] f[i, j] f[i,j] 为表示 s s s 从索引 i i i 到索引 j j j 的子串是否为回文子串的 bool 值。
则该问题的状态转移方程为:
f [ i , j ] = { t r u e , i = j t r u e , i + 1 = j  且  c i = c j f [ i + 1 , j − 1 ] , i + 1 < j  且  c i = c j f a l s e , c i ≠ c j f[i, j] = \begin{cases} true, & i = j \\ true, & i + 1 = j \ 且\ c_i = c_j \\ f[i + 1, j - 1], & i + 1 < j \ 且\ c_i = c_j \\ false, & c_i \neq c_j \end{cases} f[i,j]= true,true,f[i+1,j1],false,i=ji+1=j  ci=cji+1<j  ci=cjci=cj

在代码实现时,先令所有长度为 1 的子串的 f [ i , i ] f[i, i] f[i,i] t r u e true true ,然后从长度为 2 的子串开始遍历(第一层循环),对于每个子串长度 l l l ,在字符串上从左到右遍历所有长度为 l l l 的子串(第二层循环),求它们的 f [ i , j ] f[i, j] f[i,j]

6. 正则表达式匹配

该问题是《剑指 Offer》中的第19题,也是 LeetCode 中的第10题,在 LeetCode 中的难度为困难。
LeetCode 10. 正则表达式匹配

问题描述

给定一个字符串 s s s 和一个字符规律 p p p ,请实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

  • ‘.’ 匹配任意单个字符
  • ‘*’ 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖整个字符串 s s s ,而不是部分字符串。

解法

s i s_i si p j p_j pj 分别为 s s s p p p 中的第 i i i j j j 个字符, f [ i , j ] f[i, j] f[i,j] 为表示字符串 s s s 的前 i i i 个字符和正则表达式 p p p 的前 j j j 个字符是否匹配的 bool 值。
则该问题的状态转移方程为:
f [ i , j ] = { i f    p j ≠ ∗ { f [ i − 1 , j − 1 ] , s i = p j f a l s e , s i ≠ p j e l s e { f [ i − 1 , j ]   ∣ ∣   f [ i , j − 2 ] , s i = p j − 1 f [ i , j − 2 ] , s i ≠ p j − 1 f[i, j] = \begin{cases} if \ \ p_j \neq * \begin{cases} f[i - 1, j - 1], & s_i = p_j \\ false, & s_i \neq p_j \end{cases} \\ else \begin{cases} f[i - 1, j] \ || \ f[i, j - 2], & s_i = p_{j - 1} \\ f[i, j - 2], & s_i \neq p_{j - 1} \end{cases} \end{cases} f[i,j]= if  pj={f[i1,j1],false,si=pjsi=pjelse{f[i1,j] ∣∣ f[i,j2],f[i,j2],si=pj1si=pj1

注意:状态转移方程中, s i s_i si p j p_j pj 的“等于”关系(如 s i = p j s_i = p_j si=pj )不是简单的“=”,而是“二者可以匹配”,也就是表示 s i s_i si p j p_j pj 的值相等或者 p j p_j pj 的值为“.”。

7. 连续子数组的最大和

该问题是《剑指 Offer》中的第42题,也是 LeetCode 中的第53题,在 LeetCode 中的难度为简单。
LeetCode 53. 最大子数组和

问题描述

给定一个整数数组 n u m s nums nums ,请找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和(子数组是数组中的一个连续部分)。

解法

a i a_i ai 为数组 n u m s nums nums 的第 i i i 个元素的值, f [ i ] f[i] f[i] 为以第 i i i 个元素结尾的子数组的最大和。
则该问题的状态转移方程为:
f [ i ] = { a i , i = 0 a i , i > 0  且  f [ i − 1 ] ⩽ 0 a i + f [ i − 1 ] , i > 0  且  f [ i − 1 ] > 0 f[i] = \begin{cases} a_i, & i = 0 \\ a_i, & i > 0 \ 且\ f[i - 1] \leqslant 0 \\ a_i + f[i - 1], & i > 0 \ 且\ f[i - 1] > 0 \end{cases} f[i]= ai,ai,ai+f[i1],i=0i>0  f[i1]0i>0  f[i1]>0

8. 把数字翻译成字符串

该问题是《剑指 Offer》中的第46题,在 LeetCode 中的难度为中等。
剑指 Offer 46. 把数字翻译成字符串

问题描述

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

解法

f [ i ] f[i] f[i] 为由原数字的第 0 − i 0 - i 0i 位组成的数字的翻译方法的总数, s [ i − 1 , i ] s[i - 1, i] s[i1,i] 为原数字的第 i − 1 i - 1 i1 位与第 i i i 位组成的两位数。
则该问题的状态转移方程为:
f [ i ] = { f [ i − 1 ] + f [ i − 2 ] , i > 0  且  10 ⩽ s [ i − 1 , i ] ⩽ 25 f [ i − 1 ] , e l s e f[i] = \begin{cases} f[i - 1] + f[i - 2], & i > 0 \ 且\ 10 \leqslant s[i - 1, i] \leqslant 25 \\ f[i - 1], & else \end{cases} f[i]={f[i1]+f[i2],f[i1],i>0  10s[i1,i]25else

  • 在代码实现时,令 f [ − 1 ] = 1 、 f [ 0 ] = 1 f[-1] = 1、 f[0] = 1 f[1]=1f[0]=1 ,然后从 f [ 1 ] f[1] f[1] 开始遍历
  • f [ i ] f[i] f[i] 也可以设为由原数字的第 i i i 位到最后一位组成的数字的翻译方法的总数,并据此写出从左向右的状态转移方程,从左向右与从右向左的方法是完全对称的。

9. 礼物的最大价值

该问题是《剑指 Offer》中的第47题,在 LeetCode 中的难度为中等。
剑指 Offer 47. 礼物的最大价值

问题描述

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算最多能拿到多少价值的礼物?

解法

f [ i ] [ j ] f[i][j] f[i][j] 为从第 i i i 行第 j j j 列的格子出发移动到右下角最多能拿到的礼物价值, g [ i ] [ j ] g[i][j] g[i][j] 为第 i i i 行第 j j j 列的格子中的礼物的价值。
则该问题的状态转移方程为:
f [ i ] [ j ] = { g [ i ] [ j ] + max ⁡ { f [ i + 1 ] [ j ] , f [ i ] [ j + 1 ] } , i + 1 < m  且  j + 1 < n g [ i ] [ j ] + f [ i + 1 ] [ j ] , i + 1 < m  且  j + 1 = n g [ i ] [ j ] + f [ i ] [ j + 1 ] , i + 1 = m  且  j + 1 < n g [ i ] [ j ] , i + 1 = m  且  j + 1 = n f[i][j] = \begin{cases} g[i][j] + \max \{ f[i + 1][j], f[i][j + 1] \}, & i + 1 < m \ 且\ j + 1 < n \\ g[i][j] + f[i + 1][j], & i + 1 < m \ 且\ j + 1 = n \\ g[i][j] + f[i][j + 1], & i + 1 = m \ 且\ j + 1 < n \\ g[i][j], & i + 1 = m \ 且\ j + 1 = n \end{cases} f[i][j]= g[i][j]+max{f[i+1][j],f[i][j+1]},g[i][j]+f[i+1][j],g[i][j]+f[i][j+1],g[i][j],i+1<m  j+1<ni+1<m  j+1=ni+1=m  j+1<ni+1=m  j+1=n

  • 在代码实现时,可以直接将 g [ i ] [ j ] g[i][j] g[i][j] 作为 f [ i ] [ j ] f[i][j] f[i][j] 使用,从而减少空间占用,将空间复杂度从 O ( n ) O(n) O(n) 降低至 O ( 1 ) O(1) O(1)

10. 丑数

该问题是《剑指 Offer》中的第49题,也是 LeetCode 中的第264题,在 LeetCode 中的难度为中等。
剑指 Offer 49. 丑数

问题描述

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

解法

f [ i ] f[i] f[i] 为第 i i i 个丑数, p 2 p_2 p2 p 3 p_3 p3 p 5 p_5 p5 是三个指针(开始时都指向第一个丑数 1 1 1 ),分别指向下一个要乘 2、3、5 的数。
则该问题的状态转移方程为:
f [ i ] = min ⁡ { 2 × f [ p 2 ] , 3 × f [ p 3 ] , 5 × f [ p 5 ] } f[i] = \min \{ 2 \times f[p_2], 3 \times f[p_3], 5 \times f[p_5] \} f[i]=min{2×f[p2],3×f[p3],5×f[p5]}

在代码实现时,循环求 f [ i ] f[i] f[i] ,在每次循环中,检查三个指针,若 f [ i ] = k × p k f[i] = k \times p_k f[i]=k×pk ,则将 p k p_k pk 加一,从而防止 f f f 中记入重复的丑数。

11. n个骰子的点数

该问题是《剑指 Offer》中的第60题,在 LeetCode 中的难度为中等。
剑指 Offer 60. n个骰子的点数

问题描述

把 n 个骰子扔在地上,所有骰子朝上一面的点数之和为 s。输入 n,打印出 s 的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。

解法

设用 n n n 个骰子扔出点数和 x x x 的概率为 f [ n , x ] f[n, x] f[n,x]
则该问题的状态转移方程为:
f [ n , x ] = ∑ i = 1 6 ( f [ n − 1 , x − i ] × 1 6 ) f[n, x] = \sum_{i = 1}^{6} (f[n - 1, x - i] \times \frac{1}{6}) f[n,x]=i=16(f[n1,xi]×61)

在代码实现时, f [ n − 1 , x − i ] f[n - 1, x - i] f[n1,xi] 会出现三种非法情况:

  • x < i x < i x<i :总点数 < < < i i i 个骰子的点数
  • x − i > 6 ( n − 1 ) x - i > 6 (n - 1) xi>6(n1) :所需的前 n − 1 n - 1 n1 个骰子的最大总和 > > > 前面 n − 1 n - 1 n1 个骰子的可能的最大总和
  • x − i < ( n − 1 ) x - i < (n - 1) xi<(n1) :所需的前 n − 1 n - 1 n1 个骰子的最大总和 < < < 前面 n − 1 n - 1 n1 个骰子的可能的最小总和

三种非法的 f [ n − 1 , x − i ] f[n - 1, x - i] f[n1,xi] 的值取0即可。

可以采用正向的逻辑编写代码,即对于有 n n n 个骰子的情况,不依次求 f [ n , x ] f[n, x] f[n,x] ,而是依次计算 f [ n − 1 , x ] f[n - 1, x] f[n1,x] 对于每个 f [ n , x ] f[n, x] f[n,x] 的贡献,这样就无需考虑三种非法的 f [ n − 1 , x − i ] f[n - 1, x - i] f[n1,xi] 情况,在时间效率和代码复杂度上都更优。

12. 买卖股票的最佳时机

该问题是《剑指 Offer》中的第63题,也是 LeetCode 中的第121题,在 LeetCode 中的难度为中等。
121. 买卖股票的最佳时机

问题描述

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

解法

解法一

p i p_i pi 表示第 i i i 天的价格,共有 n n n 天, f [ i ] f[i] f[i] 表示前 i i i 天中价格最低的那天的价格(史低)。
则该问题的状态转移方程为:
f [ i ] = { p i , i = 0 min ⁡ { f [ i − 1 ] , p i } , i > 0 f[i] = \begin{cases} p_i , & i = 0 \\ \min \{ f[i - 1], p_i \}, & i > 0 \end{cases} f[i]={pi,min{f[i1],pi},i=0i>0
因此,该问题的解即为 max ⁡ { p i − f [ i ]   ∣   i = 1 , … , n } \max \{ p_i - f[i] \ | \ i = 1, \dots, n \} max{pif[i]  i=1,,n}

解法二

p i p_i pi 表示第 i i i 天的价格,共有 n n n 天, g [ i ] g[i] g[i] 表示在第 i i i 天卖出股票可以获得的最大利润。
则该问题的状态转移方程为:
g [ i ] = { 0 , i = 0 max ⁡ { p i − p i − 1 , 0 } , i > 0  且  g [ i − 1 ] = 0 max ⁡ { g [ i − 1 ] + p i − p i − 1 , 0 } , i > 0  且  g [ i − 1 ] > 0 g[i] = \begin{cases} 0 , &i = 0 \\ \max \{ p_i - p_{i - 1}, 0 \} , & i > 0 \ 且\ g[i - 1] = 0 \\ \max \{ g[i - 1] + p_i - p_{i - 1}, 0 \}, & i > 0 \ 且\ g[i - 1] > 0 \end{cases} g[i]= 0,max{pipi1,0},max{g[i1]+pipi1,0},i=0i>0  g[i1]=0i>0  g[i1]>0
因此,该问题的解即为 max ⁡ { g [ i ]   ∣   i = 1 , … , n } \max \{ g[i] \ | \ i = 1, \dots, n \} max{g[i]  i=1,,n}

显然,解法二不如解法一。

13. 买卖股票的最佳时机 II

该问题是 LeetCode 中的第122题,在 LeetCode 中的难度为中等。
122. 买卖股票的最佳时机 II

问题描述

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候最多只能持有一股股票。你也可以先购买,然后在同一天出售。
返回你能获得的最大利润 。

解法

p i p_i pi 表示第 i i i 天的价格,共有 n n n 天, f [ i ] f[i] f[i] 表示前 i i i 天能够获得的最大利润。
则该问题的状态转移方程为:
f [ i ] = { p i , i = 0 f [ i − 1 ] , i > 0  且  p i − 1 ⩾ p i f [ i − 1 ] + p i − p i − 1 , i > 0  且  p i − 1 < p i f[i] = \begin{cases} p_i , & i = 0 \\ f[i - 1], & i > 0 \ 且\ p_{i - 1} \geqslant p_i \\ f[i - 1] + p_i - p_{i - 1}, & i > 0 \ 且\ p_{i - 1} < p_i \end{cases} f[i]= pi,f[i1],f[i1]+pipi1,i=0i>0  pi1pii>0  pi1<pi

14. 剪绳子

该问题是《剑指 Offer》中的第14题,也是 LeetCode 中的第343题,在 LeetCode 中的难度为中等。
剑指 Offer 14- I. 剪绳子

问题描述

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

解法

f [ i ] f[i] f[i] 表示长度为 i i i 的绳子可以得到的最大乘积。
则该问题的状态转移方程为:
f [ i ] = max ⁡ { max ⁡ { f [ i − j ] ∗ j   ∣   j = 1 , … , i − 1 } , i } f[i] = \max \{\max \{ f[i - j] * j \ | \ j = 1, \dots, i - 1 \}, i \} f[i]=max{max{f[ij]j  j=1,,i1},i}

15. 最长递增子序列

该问题是 LeetCode 中的第300题,在 LeetCode 中的难度为中等。
300. 最长递增子序列

问题描述

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

解法

a i a_i ai 为数组 n u m s nums nums 的第 i i i 个元素的值, f [ i ] f[i] f[i] 表示以 a i a_i ai 为结尾的最长子序列的长度。
则该问题的状态转移方程为:
f [ i ] = { 1 , i = 0 max ⁡ { f [ j ]   ∣   j = 1 , … , i − 1  且  a j < a i  且  f [ j ] ⩾ f [ i ] } + 1 , i > 0 f[i] = \begin{cases} 1, & i = 0 \\ \max \{ f[j] \ | \ j = 1, \dots, i - 1 \ 且\ a_j < a_i \ 且\ f[j] \geqslant f[i] \} + 1, & i > 0 \end{cases} f[i]={1,max{f[j]  j=1,,i1  aj<ai  f[j]f[i]}+1,i=0i>0

16. 编辑距离

该问题是 LeetCode 中的第72题,在 LeetCode 中的难度为困难。
LeetCode 72. 编辑距离

问题描述

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数。
你可以对一个单词进行如下三种操作:

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

解法

s i s_i si p j p_j pj 分别为 w o r d 1 word1 word1 w o r d 2 word2 word2 中的第 i i i j j j 个字符, f [ i , j ] f[i, j] f[i,j] 为将 w o r d 1 word1 word1 的前 i i i 个字符转换成 w o r d 2 word2 word2 的前 j j j 个字符所需要的最少操作数。
则该问题的状态转移方程为:
f [ i , j ] = { i , j = 0 j , i = 0 f [ i − 1 ] [ j − 1 ] , s j = p j min ⁡ { f [ i ] [ j − 1 ] , f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] } + 1 , s j ≠ p j f[i, j] = \begin{cases} i, & j = 0 \\ j, & i = 0 \\ f[i - 1][j - 1], & s_j = p_j \\ \min \{ f[i][j - 1], f[i - 1][j], f[i - 1][j - 1] \} + 1, & s_j \neq p_j \end{cases} f[i,j]= i,j,f[i1][j1],min{f[i][j1],f[i1][j],f[i1][j1]}+1,j=0i=0sj=pjsj=pj

注:若 s i s_i si p j p_j pj 相等,则该位置无需操作;若 s i s_i si p j p_j pj 不相等,则取插入、删除、更改三种操作中的最小值。

17. 甲板上的战舰

该问题是 LeetCode 中的第419题,在 LeetCode 中的难度为中等。
LeetCode 419. 甲板上的战舰

问题描述

给你一个大小为 m x n 的矩阵 board 表示甲板,其中,每个单元格可以是一艘战舰 ‘X’ 或者是一个空位 ‘.’ ,返回在甲板 board 上放置的战舰 的数量。
战舰只能水平或者垂直放置在 board 上。换句话说,战舰只能按 1 x k(1 行,k 列)或 k x 1(k 行,1 列)的形状建造,其中 k 可以是任意大小。两艘战舰之间至少有一个水平或垂直的空位分隔(即没有相邻的战舰)。

解法

b i , j b_{i,j} bi,j 为甲板 b o a r d board board 上第 i i i 行第 j j j 列的位置的字符, f [ i , j ] f[i,j] f[i,j] 表示以第 i i i 行第 j j j 列的位置为右下角的子甲板上的战舰数量。
则该问题的状态转移方程为:
f [ i , j ] = { i f            i = 0  且  j = 0                           { 1 ,                                                                          b i , j = X 0 ,                                                                          b i , j = . e l s e    i f    i = 0  且  j > 0 { i f    b i , j = X { f [ i , j − 1 ] ,                                                    b i , j − 1 = X f [ i , j − 1 ] + 1 ,                                                    b i , j − 1 = . e l s e                   f [ i , j − 1 ] e l s e    i f    i > 0  且  j = 0 { i f    b i , j = X { f [ i − 1 , j ] ,                                                    b i − 1 , j = X f [ i − 1 , j ] + 1 ,                                                    b i − 1 , j = . e l s e                   f [ i − 1 , j ] e l s e                                  { i f    b i , j = X { f [ i − 1 , j ] + f [ i , j − 1 ] − f [ i − 1 , j − 1 ] + 1 , b i , j − 1 = .    且  b i − 1 , j = . f [ i − 1 , j ] + f [ i , j − 1 ] − f [ i − 1 , j − 1 ] , b i , j − 1 = X  或  b i − 1 , j = X e l s e                   f [ i − 1 , j ] + f [ i , j − 1 ] − f [ i − 1 , j − 1 ] f[i,j] = \begin{cases} if \ \ \ \ \ \ \ \ \ \ i = 0 \ 且 \ j = 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \begin{cases} 1, & \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ b_{i,j}=X \\ 0, & \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ b_{i,j}=. \end{cases} \\ else \ \ if \ \ i = 0 \ 且 \ j > 0 \begin{cases} if \ \ b_{i,j}=X \begin{cases} f[i,j-1], &\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ b_{i,j-1}=X \\ f[i,j-1] + 1, &\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ b_{i,j-1}=. \end{cases} \\ else \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i,j-1] \end{cases} \\ else \ \ if \ \ i > 0 \ 且 \ j = 0 \begin{cases} if \ \ b_{i,j}=X \begin{cases} f[i-1,j], & \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ b_{i-1,j}=X \\ f[i-1,j] + 1, & \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ b_{i-1,j}=. \end{cases} \\ else \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1,j] \end{cases} \\ else \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \begin{cases} if \ \ b_{i,j}=X \begin{cases} f[i-1,j] + f[i,j-1] - f[i-1,j-1] + 1, & b_{i,j-1}=. \ \ \ 且 \ b_{i-1,j}=. \\ f[i-1,j] + f[i,j-1] - f[i-1,j-1], & b_{i,j-1}=X \ 或 \ b_{i-1,j}=X \end{cases} \\ else \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ f[i-1,j] + f[i,j-1] - f[i-1,j-1] \end{cases} \\ \end{cases} f[i,j]= if          i=0  j=0                         {1,0,                                                                        bi,j=X                                                                        bi,j=.else  if  i=0  j>0 if  bi,j=X{f[i,j1],f[i,j1]+1,                                                  bi,j1=X                                                  bi,j1=.else                 f[i,j1]else  if  i>0  j=0 if  bi,j=X{f[i1,j],f[i1,j]+1,                                                  bi1,j=X                                                  bi1,j=.else                 f[i1,j]else                                 if  bi,j=X{f[i1,j]+f[i,j1]f[i1,j1]+1,f[i1,j]+f[i,j1]f[i1,j1],bi,j1=.    bi1,j=.bi,j1=X  bi1,j=Xelse                 f[i1,j]+f[i,j1]f[i1,j1]

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZBH4444

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值