一、多维动态规划问题基础
- 一般流程
(1)首先,需要定义一个多维dp数组,明确每个下标含义
(2)其次,需要确定状态转移方程,如,dp[i,j]=dp[i-1,j]+dp[j,j-1]
(3)还有,需要初始化状态
(4)注意,状态转移时,注意边界条件,下标要从1开始算起 - m = string.length() n=vector.size()
二、62. 不同路径
1 题目
2 解题思路
(1)状态转移方程:想要走到(i,j),只能从(i-1,j),(i,j-1)走过来。因此,我们可以写出动态规划方程:dp[i,j]=dp[i-1,j]+dp[j,j-1]
(2)初始化状态:如果i=0,相当于只有一行的路径,所以dp[0][j]=1;如果j=0 相当于只有一列的路径,所以dp[i][0]=1
3 code
class Solution {
public:
int uniquePaths(int m, int n)
{
// 定义二dp维数组
vector<vector<int>> dp(m,vector<int>(n,0));
// 初始化状态
for(int i=0;i<m;i++)
{
dp[i][0]=1;
}
for(int j=0;j<n;j++)
{
dp[0][j]=1;
}
// 状态转移
// i,j从1开始
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)
{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
三、64. 最小路径和
1 题目
2 解题思路
(1)状态转移方程:dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]
(2)初始化状态:如果i=0,相当于只有一行的路径,所以dp[0][j]=dp[0][j-1]+grid[0][j];如果j=0 相当于只有一列的路径,所以dp[i][0]=dp[i-1][0]+grid[i][0];dp[0][0]=grid[0][0]
3 code
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
// 定义一个dp二维数组
int m =grid.size();
int n = grid[0].size();
vector<vector<int>> dp(m,vector<int>(n,0));
// 初始化状态
dp[0][0]=grid[0][0];
for(int i=1;i<m;i++) dp[i][0]=dp[i-1][0]+grid[i][0];
for(int j=1;j<n;j++) dp[0][j]=dp[0][j-1]+grid[0][j];
// 状态转移
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)
{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[m-1][n-1];
}
};
四、5. 最长回文子串
1 题目
2 解题思路
(1)如果两个端点字符相等s[i]=s[j],就检查各自的下一位是否相等s[i+1]=s[j-1]?
(2)状态转移:1.两个端点要相等是s[i]=s[j],其次,s[j+1:i-1]也要为回文串
(3)初始化状态:如果j=i,子串长度为1,肯定是回文串;当j=i+1,子串长度位2,只要首尾两个端点相等,就是回文串
3 code
class Solution {
public:
string longestPalindrome(string s) {
// 定义一个dp二维数组
int n = s.size();
int length = 1; //最长回文子串的长度
int start = 0; //最长回文子串的起点
// dp[i][j]表示子串s[i:j]是否位回文子串
vector<vector<bool>> dp(n,vector<bool>(n));
for(int i=0;i<n;i++)
{
for(int j=i;j>=0;j--)
{ // 以i位起点,往回枚举起点j
if(i==j)
{// 情况一:i,j相等,子串长度为1
dp[j][i]=true;
}
else if(i==j+1)
{// 情况一:j=i-1,子串长度为2
dp[j][i]= (s[i]==s[j]);
}
else
{// 情况三:一般情况,子串长度大于等于3
if(s[i]==s[j])
{
dp[j][i]=dp[j+1][i-1];
}
else
{
dp[j][i]=false;
}
}
// 更新最长回文子串
if(dp[j][i] && i-j+1 > length)
{
length=i-j+1;
start=j;
}
}
}
return s.substr(start,length);
}
};
五、1143. 最长公共子序列
1 题目
2 解题思路
(1)状态定义:定义dp[i][j]表示text1的前i个字符和text2的前j个字符之间最长公共子序列的长度
(2)状态转移:dp[i][j]的状态只能由dp[i-1][j-1],dp[i][k-1],dp[i-1][j]转移而来。
-
当text1[i]==text2[j]
dp[i][j]状态由dp[i-1][j-1]转移而来,dp[i][j]=dp[i-1][j-1]+1 -
当text[i]!=text2[j]
dp[i][j]状态由dp[i-1][j]和dp[i][j-1]状态中较大的而来,dp[i][j]=max(dp[i-1][j],dp[i][j-1])
3 code
class Solution {
public:
int longestCommonSubsequence(string text1, string text2)
{
int n = text1.size(), m = text2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (text1[i] == text2[j])
{
dp[i+1][j+1] = dp[i][j] + 1;
}
else
{
dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j]);
}
}
}
return dp[n][m];
}
};
六、72. 编辑距离
1 题目
2 解题思路
(1)定义一个dp二维数组:dp[i][j]代表word1到i位置转换成word2到j位置需要最少步数
(2)状态转移: 当word1[i]==word2[j],dp[i][j]=dp[i-1][j-1]+1 当word1[i] != word2[j],dp[i][j]=min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+1,其中,dp[i-1][j-1]表示替换操作,dp[i-1][j]表示删除操作,dp[i][j-1]表示插入操作
(3)状态初始化,i=0时,怎么处理,i=j时,怎么处理
3 code
class Solution {
public:
int minDistance(string word1, string word2) {
vector<vector<int>> dp(word1.size()+1, vector<int>(word2.size()+1,0));
// 状态初始化
for(int i=0;i<dp.size();i++){
dp[i][0]=i;
}
for(int j=0;j<dp[0].size();j++){
dp[0][j]=j;
}
// 状态转移,i,j从1开始便利
for(int i=1;i<dp.size();i++){
for(int j=1;j<dp[i].size();j++){
if(word1[i-1]==word2[j-1]){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=min(dp[i-1][j-1],min(dp[i][j-1],dp[i-1][j]))+1;
}
}
}
return dp[word1.size()][word2.size()];
}
};