LeetCode 63: Unique Paths II (Grid DP with Obstacles)
LeetCode 63Dynamic ProgrammingGridInterviewToday we solve LeetCode 63 - Unique Paths II.
Source: https://leetcode.com/problems/unique-paths-ii/
English
Problem Summary
Given an m x n grid where 1 means obstacle and 0 means empty cell, count distinct paths from top-left to bottom-right. You can only move right or down.
Key Insight
Same DP shape as LeetCode 62, but blocked cells force path count to 0. For open cells: dp[r][c] = dp[r-1][c] + dp[r][c-1].
Brute Force and Limitations
DFS enumerating all routes is exponential and re-computes many subproblems. DP stores each cell's answer once.
Optimal Algorithm Steps
1) If start is obstacle, return 0 immediately.
2) Initialize dp[0][0] = 1.
3) Traverse row by row.
4) If cell is obstacle, set dp[r][c] = 0.
5) Otherwise add paths from top and left when in range.
Complexity Analysis
Time: O(mn).
Space: O(mn) (can be optimized to O(n)).
Common Pitfalls
- Forgetting to handle obstacle at start/end.
- Incorrect first row/column handling when obstacle appears.
- Writing transition before obstacle check.
- Overflow assumptions in languages with fixed integer sizes.
Reference Implementations (Java / Go / C++ / Python / JavaScript)
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length, n = obstacleGrid[0].length;
if (obstacleGrid[0][0] == 1) return 0;
int[][] dp = new int[m][n];
dp[0][0] = 1;
for (int r = 0; r < m; r++) {
for (int c = 0; c < n; c++) {
if (obstacleGrid[r][c] == 1) {
dp[r][c] = 0;
continue;
}
if (r > 0) dp[r][c] += dp[r - 1][c];
if (c > 0) dp[r][c] += dp[r][c - 1];
}
}
return dp[m - 1][n - 1];
}
}func uniquePathsWithObstacles(obstacleGrid [][]int) int {
m, n := len(obstacleGrid), len(obstacleGrid[0])
if obstacleGrid[0][0] == 1 {
return 0
}
dp := make([][]int, m)
for i := 0; i < m; i++ {
dp[i] = make([]int, n)
}
dp[0][0] = 1
for r := 0; r < m; r++ {
for c := 0; c < n; c++ {
if obstacleGrid[r][c] == 1 {
dp[r][c] = 0
continue
}
if r > 0 {
dp[r][c] += dp[r-1][c]
}
if c > 0 {
dp[r][c] += dp[r][c-1]
}
}
}
return dp[m-1][n-1]
}class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size(), n = obstacleGrid[0].size();
if (obstacleGrid[0][0] == 1) return 0;
vector<vector<int>> dp(m, vector<int>(n, 0));
dp[0][0] = 1;
for (int r = 0; r < m; ++r) {
for (int c = 0; c < n; ++c) {
if (obstacleGrid[r][c] == 1) {
dp[r][c] = 0;
continue;
}
if (r > 0) dp[r][c] += dp[r - 1][c];
if (c > 0) dp[r][c] += dp[r][c - 1];
}
}
return dp[m - 1][n - 1];
}
};class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: list[list[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
if obstacleGrid[0][0] == 1:
return 0
dp = [[0] * n for _ in range(m)]
dp[0][0] = 1
for r in range(m):
for c in range(n):
if obstacleGrid[r][c] == 1:
dp[r][c] = 0
continue
if r > 0:
dp[r][c] += dp[r - 1][c]
if c > 0:
dp[r][c] += dp[r][c - 1]
return dp[m - 1][n - 1]var uniquePathsWithObstacles = function(obstacleGrid) {
const m = obstacleGrid.length;
const n = obstacleGrid[0].length;
if (obstacleGrid[0][0] === 1) return 0;
const dp = Array.from({ length: m }, () => Array(n).fill(0));
dp[0][0] = 1;
for (let r = 0; r < m; r++) {
for (let c = 0; c < n; c++) {
if (obstacleGrid[r][c] === 1) {
dp[r][c] = 0;
continue;
}
if (r > 0) dp[r][c] += dp[r - 1][c];
if (c > 0) dp[r][c] += dp[r][c - 1];
}
}
return dp[m - 1][n - 1];
};中文
题目概述
给定一个 m x n 网格,1 表示障碍,0 表示可走格子。机器人从左上角走到右下角,只能向右或向下,求不同路径数量。
核心思路
和 LeetCode 62 的状态定义一致,但遇到障碍时该格路径数必须为 0。非障碍格转移:dp[r][c] = dp[r-1][c] + dp[r][c-1]。
暴力解法与不足
DFS 会重复计算大量子问题,时间复杂度呈指数增长。DP 把每个格子的路径数只算一次,效率稳定。
最优算法步骤
1)若起点是障碍,直接返回 0。
2)初始化 dp[0][0] = 1。
3)按行遍历网格。
4)若当前格是障碍,置 dp[r][c] = 0。
5)否则累加上方与左方路径数(若存在)。
复杂度分析
时间复杂度:O(mn)。
空间复杂度:O(mn)(可优化到 O(n))。
常见陷阱
- 忘记处理起点/终点为障碍的情况。
- 第一行或第一列遇到障碍后仍错误地继续继承路径。
- 在障碍判断之前先做状态转移,导致污染。
- 未考虑整数范围(某些语言整型上限)。
多语言参考实现(Java / Go / C++ / Python / JavaScript)
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length, n = obstacleGrid[0].length;
if (obstacleGrid[0][0] == 1) return 0;
int[][] dp = new int[m][n];
dp[0][0] = 1;
for (int r = 0; r < m; r++) {
for (int c = 0; c < n; c++) {
if (obstacleGrid[r][c] == 1) {
dp[r][c] = 0;
continue;
}
if (r > 0) dp[r][c] += dp[r - 1][c];
if (c > 0) dp[r][c] += dp[r][c - 1];
}
}
return dp[m - 1][n - 1];
}
}func uniquePathsWithObstacles(obstacleGrid [][]int) int {
m, n := len(obstacleGrid), len(obstacleGrid[0])
if obstacleGrid[0][0] == 1 {
return 0
}
dp := make([][]int, m)
for i := 0; i < m; i++ {
dp[i] = make([]int, n)
}
dp[0][0] = 1
for r := 0; r < m; r++ {
for c := 0; c < n; c++ {
if obstacleGrid[r][c] == 1 {
dp[r][c] = 0
continue
}
if r > 0 {
dp[r][c] += dp[r-1][c]
}
if c > 0 {
dp[r][c] += dp[r][c-1]
}
}
}
return dp[m-1][n-1]
}class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size(), n = obstacleGrid[0].size();
if (obstacleGrid[0][0] == 1) return 0;
vector<vector<int>> dp(m, vector<int>(n, 0));
dp[0][0] = 1;
for (int r = 0; r < m; ++r) {
for (int c = 0; c < n; ++c) {
if (obstacleGrid[r][c] == 1) {
dp[r][c] = 0;
continue;
}
if (r > 0) dp[r][c] += dp[r - 1][c];
if (c > 0) dp[r][c] += dp[r][c - 1];
}
}
return dp[m - 1][n - 1];
}
};class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: list[list[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
if obstacleGrid[0][0] == 1:
return 0
dp = [[0] * n for _ in range(m)]
dp[0][0] = 1
for r in range(m):
for c in range(n):
if obstacleGrid[r][c] == 1:
dp[r][c] = 0
continue
if r > 0:
dp[r][c] += dp[r - 1][c]
if c > 0:
dp[r][c] += dp[r][c - 1]
return dp[m - 1][n - 1]var uniquePathsWithObstacles = function(obstacleGrid) {
const m = obstacleGrid.length;
const n = obstacleGrid[0].length;
if (obstacleGrid[0][0] === 1) return 0;
const dp = Array.from({ length: m }, () => Array(n).fill(0));
dp[0][0] = 1;
for (let r = 0; r < m; r++) {
for (let c = 0; c < n; c++) {
if (obstacleGrid[r][c] === 1) {
dp[r][c] = 0;
continue;
}
if (r > 0) dp[r][c] += dp[r - 1][c];
if (c > 0) dp[r][c] += dp[r][c - 1];
}
}
return dp[m - 1][n - 1];
};
Comments