Day34 动态规划:62.不同路径 63.不同路径Ⅱ

107 阅读3分钟

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数组

34.01.png

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

34.02.png

下标 (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数组

34.03.png

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