Medium

题目描述

给你一个 m x n 的整数数组 grid。一个机器人初始位于左上角(即 grid[0][0])。机器人尝试移动到右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

网格中的障碍物和空位置分别用 10 来表示。机器人的移动路径不能包含任何有障碍物的方格。

返回机器人能够到达右下角的不同路径数量。

测试用例保证答案小于等于 2 * 10^9

示例 1:

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

示例 2:

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j]01

解题思路

这是一道经典的动态规划问题,是"不同路径"问题的变种,增加了障碍物的限制。

核心思路:

  1. 状态定义dp[i][j] 表示从起点 (0,0) 到达位置 (i,j) 的不同路径数量。

  2. 状态转移

    • 如果 obstacleGrid[i][j] == 1(有障碍物),则 dp[i][j] = 0
    • 如果 obstacleGrid[i][j] == 0(无障碍物),则 dp[i][j] = dp[i-1][j] + dp[i][j-1]
  3. 边界处理

    • 如果起点就有障碍物,直接返回 0
    • 第一行和第一列需要特殊处理,因为只能从一个方向到达
  4. 优化方案

    • 方案一:使用二维 DP 数组,空间复杂度 O(mn)
    • 方案二:使用一维 DP 数组,空间复杂度 O(n)(推荐)
    • 方案三:原地修改输入数组(如果允许的话)

算法步骤

  1. 检查起点是否有障碍物
  2. 初始化 DP 数组
  3. 按行遍历,根据状态转移方程更新 DP 值
  4. 返回右下角的路径数

代码实现

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        
        // 如果起点或终点有障碍物,直接返回0
        if (obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1) {
            return 0;
        }
        
        // 使用一维DP数组优化空间复杂度
        vector<int> dp(n, 0);
        dp[0] = 1;
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (obstacleGrid[i][j] == 1) {
                    dp[j] = 0;
                } else if (j > 0) {
                    dp[j] += dp[j-1];
                }
            }
        }
        
        return dp[n-1];
    }
};
class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        
        # 如果起点或终点有障碍物,直接返回0
        if obstacleGrid[0][0] == 1 or obstacleGrid[m-1][n-1] == 1:
            return 0
        
        # 使用一维DP数组优化空间复杂度
        dp = [0] * n
        dp[0] = 1
        
        for i in range(m):
            for j in range(n):
                if obstacleGrid[i][j] == 1:
                    dp[j] = 0
                elif j > 0:
                    dp[j] += dp[j-1]
        
        return dp[n-1]
public class Solution {
    public int UniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.Length;
        int n = obstacleGrid[0].Length;
        
        // 如果起点或终点有障碍物,直接返回0
        if (obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1) {
            return 0;
        }
        
        // 使用一维DP数组优化空间复杂度
        int[] dp = new int[n];
        dp[0] = 1;
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (obstacleGrid[i][j] == 1) {
                    dp[j] = 0;
                } else if (j > 0) {
                    dp[j] += dp[j-1];
                }
            }
        }
        
        return dp[n-1];
    }
}
/**
 * @param {number[][]} obstacleGrid
 * @return {number}
 */
var uniquePathsWithObstacles = function(obstacleGrid) {
    const m = obstacleGrid.length;
    const n = obstacleGrid[0].length;
    
    // 如果起点或终点有障碍物,直接返回0
    if (obstacleGrid[0][0]

复杂度分析

复杂度类型二维DP一维DP(推荐)
时间复杂度O(mn)O(mn)
空间复杂度O(mn)O(n)

其中 m 为网格的行数,n 为网格的列数。一维DP优化方案在保持相同时间复杂度的情况下,将空间复杂度从 O(mn) 优化到 O(n)。

相关题目