LeetCode Day42

114 阅读4分钟

62. 不同路径

一个机器人位于一个 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

思路

第一步:确定dp数组和下标的含义

定义一个二维数组 (dp),其中 (dp[i][j]) 表示从起始点到网格中的点 ((i, j)) 有多少种不同的路径。

第二步:确定递推公式

由于机器人只能向下或向右移动,因此到达点 ((i, j)) 只有两种方式:

  1. 从点 ((i-1, j)) 向下移动一步。
  2. 从点 ((i, j-1)) 向右移动一步。

因此,递推公式为:

第三步:dp数组如何初始化

  • 第一行和第一列的所有点只有一种路径可以到达(要么一直向右,要么一直向下),所以第一行和第一列的 (dp) 值都应该初始化为 1。

第四步:确定遍历顺序

由于 (dp[i][j]) 是基于 (dp[i-1][j]) 和 (dp[i][j-1]) 的,所以我们应该先填充第一行和第一列,然后从左到右、从上到下遍历整个数组。

第五步:举例推导dp数组

假设 (m = 3, n = 2),则 (dp) 数组如下:

  • (dp[0][0] = 1)
  • (dp[0][1] = 1)
  • (dp[1][0] = 1)
  • (dp[1][1] = dp[0][1] + dp[1][0] = 2)
  • (dp[2][0] = 1)
  • (dp[2][1] = dp[1][1] + dp[2][0] = 3)

最终结果是 (dp[2][1] = 3)。

题解

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m, vector<int>(n, 1));
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=dp[i][j-1]+dp[i-1][j];
            }    
        }
        return dp[m-1][n-1];
    }
};

63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? 网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1: 输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] 输出:2 解释:3x3 网格的正中间有一个障碍物。 从左上角到右下角一共有 2 条不同的路径: 1. 向右 -> 向右 -> 向下 -> 向下 2. 向下 -> 向下 -> 向右 -> 向右 示例 2: 输入:obstacleGrid = [[0,1],[0,0]] 输出:1

思路

这个问题是之前网格问题的一个变种,其中添加了障碍物。依然可以使用动态规划来解决。

第一步:确定dp数组和下标的含义

定义一个二维数组 ( dp ),其中 ( dp[i][j] ) 表示从左上角(起点)到 ( (i, j) ) 的不同路径数量。

第二步:确定递推公式

如果 ( (i, j) ) 位置没有障碍物(即 ( obstacleGrid[i][j] = 0 )),则递推公式为: 如果 ( (i, j) ) 位置有障碍物(即 ( obstacleGrid[i][j] = 1 )),则 ( dp[i][j] = 0 )。

第三步:dp数组如何初始化

  • 如果起点 ( (0, 0) ) 有障碍物,则 ( dp[0][0] = 0 ),否则 ( dp[0][0] = 1 )。
  • 初始化第一行和第一列:如果有障碍物,则从那一点开始,该行或该列后面的所有值都应为 0。

第四步:确定遍历顺序

由于 ( dp[i][j] ) 是基于 ( dp[i-1][j] ) 和 ( dp[i][j-1] ) 的,所以应从左到右、从上到下遍历整个数组。

第五步:举例推导dp数组

假设 ( obstacleGrid = [[0, 0, 0], [0, 1, 0], [0, 0, 0]] ),则 ( dp ) 数组如下:

  • ( dp[0][0] = 1 )
  • ( dp[0][1] = 1 )
  • ( dp[0][2] = 1 )
  • ( dp[1][0] = 1 )
  • ( dp[1][1] = 0 ) (有障碍物)
  • ( dp[1][2] = 1 )
  • ( dp[2][0] = 1 )
  • ( dp[2][1] = 1 )
  • ( dp[2][2] = 2 )

最终结果是 ( dp[2][2] = 2 )。

题解

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<vector<long long>> dp(m, vector<long long>(n, 0));

        // 初始化起点
        dp[0][0] = (obstacleGrid[0][0] == 0) ? 1 : 0;
        
        // 初始化第一行和第一列
        for (int i = 1; i < m; ++i) {
            dp[i][0] = (obstacleGrid[i][0] == 0) ? dp[i-1][0] : 0;
        }
        for (int j = 1; j < n; ++j) {
            dp[0][j] = (obstacleGrid[0][j] == 0) ? dp[0][j-1] : 0;
        }
        
        // 动态规划
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                if (obstacleGrid[i][j] == 1) {
                    dp[i][j] = 0;
                } else {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
        }
        
        return dp[m - 1][n - 1];
    }
};