62.不同路径
题目链接:62.不同路径
难度指数:😀😐😕
本题掌握动态规划的方法就可以。(数论方法 有点非主流,很难想到。)
题目思路:(动规五部曲)
1️⃣ 确定 dp数组 以及 下标 的含义
dp[i][j] :表示从(0 ,0)出发,到 (i, j) 有 dp[i][j] 条不同的路径。
2️⃣ 🦄确定递推公式
想要求 dp[i][j] ,只能由两个方向推导出来,即 dp[i - 1][j] 和 dp[i][j - 1]。
在
dp[i - 1][j]向下走一步就是dp[i][j];在dp[i][j - 1]向右走一步就是dp[i][j]。
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
3️⃣ 🦄dp数组如何初始化
dp[i][j] 都是从其上方和左方推导而来,最上面这一行一定要初始化,最左边这一行也一定要初始化。
首先 dp[i][0] 一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么 dp[0][j]也同理
for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;
4️⃣ 🦄确定遍历顺序
dp[i][j] 都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了
本题就是从左到右,从上往下遍历
初始的数值在左边,每个状态
dp[i][j]是从左边往右移一格推导的;另一个同理
⚠️PS:并非所有题目都是这样的遍历顺序,具体情况具体分析。
5️⃣ 举例推导dp数组
AC代码: (核心代码模式)
class Solution {
public:
int uniquePaths(int m, int n) {
//确定dp数组以及下标的含义
vector<vector<int>> dp(m, vector<int>(n, 0));
//dp数组的初始化
for(int i = 0; i < m; i++) {
dp[i][0] = 1;
}
for (int j = 0; j < n; j++) {
dp[0][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];
}
}
//举例推导dp数组
return dp[m - 1][n - 1];
}
};
时间复杂度:O(m × n)
空间复杂度:O(m × n)
其实用一个一维数组(也可以理解是滚动数组)就可以了,但是不利于理解,可以优化点空间,建议先理解了二维,再理解一维。
AC代码: (核心代码模式)
class Solution {
public:
int uniquePaths(int m, int n) {
vector<int> dp(n);
for (int i = 0; i < n; i++) dp[i] = 1;
for (int j = 1; j < m; j++) {
for (int i = 1; i < n; i++) {
dp[i] += dp[i - 1];
}
}
return dp[n - 1];
}
};
时间复杂度:O(m × n)
空间复杂度:O(n)
63.不同路径Ⅱ
题目链接:63.不同路径Ⅱ
难度指数:😀😐🤨
题目思路:(动规五部曲)
1️⃣ 确定 dp数组 以及 下标 的含义
dp[i][j] :表示从 (0 ,0) 出发,到(i, j) 有dp[i][j]条不同的路径。
2️⃣ 确定递推公式
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
⚠️因为有了障碍, (i, j) 如果就是障碍的话应该就保持初始状态(初始状态为0)。
if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i][j]
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
3️⃣ dp数组如何初始化
如果(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是走不到的位置了,所以障碍之后的 dp[i][0] 应该还是初始值0
下标 (0, j) 的初始化情况同理
vector<vector<int>> dp(m, vector<int>(n, 0));
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
一旦遇到 obstacleGrid[i][0] == 1 的情况就停止dp[i][0] 的赋值1的操作,dp[0][j] 同理
4️⃣ 确定遍历顺序
5️⃣ 举例推导dp数组
AC代码: (核心代码模式)
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
if (obstacleGrid[0][0] == 1 || obstacleGrid[m - 1][n - 1] == 1) { //如果在起点或终点出现障碍物,直接返回0
return 0;
}
//确定dp数组以及下标的含义
vector<vector<int>> dp(m, vector<int>(n, 0));
//dp数组的初始化
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
dp[i][0] = 1;
}
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
dp[0][j] == 1;
}
//遍历顺序
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
//特判:遇到障碍物,直接continue
if (obstacleGrid[i][j] == 1) {
continue;
}
//递推公式
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
//举例推导dp数组
return dp[m - 1][n - 1];
}
};
自己写的代码,输出结果就是不对!!! ❌
输入:[[0,0,0],[0,1,0],[0,0,0]]
输出:1
预期结果:2
stdout:0 1 1