class Solution:
def uniquePathsWithObstacles(self, obstacleGrid):
if not obstacleGrid or obstacleGrid[0][0] == 1:
return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0]*n for _ in range(m)]
dp[0][0] = 1
for i in range(m):
for j in range(n):
if obstacleGrid[i][j] == 1:
dp[i][j] = 0
else:
if i > 0:
dp[i][j] += dp[i-1][j]
if j > 0:
dp[i][j] += dp[i][j-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 i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (obstacleGrid[i][j] == 1) {
dp[i][j] = 0;
} else {
if (i > 0) dp[i][j] += dp[i-1][j];
if (j > 0) dp[i][j] += dp[i][j-1];
}
}
}
return dp[m-1][n-1];
}
};
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 i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (obstacleGrid[i][j] == 1) {
dp[i][j] = 0;
} else {
if (i > 0) dp[i][j] += dp[i-1][j];
if (j > 0) dp[i][j] += dp[i][j-1];
}
}
}
return dp[m-1][n-1];
}
}
var uniquePathsWithObstacles = function(obstacleGrid) {
if (!obstacleGrid || obstacleGrid[0][0] === 1) return 0;
let m = obstacleGrid.length, n = obstacleGrid[0].length;
let dp = Array.from({length: m}, () => Array(n).fill(0));
dp[0][0] = 1;
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (obstacleGrid[i][j] === 1) {
dp[i][j] = 0;
} else {
if (i > 0) dp[i][j] += dp[i-1][j];
if (j > 0) dp[i][j] += dp[i][j-1];
}
}
}
return dp[m-1][n-1];
};
You are given a 2D grid represented by obstacleGrid
where each cell contains either a 0
(an empty space) or a 1
(an obstacle). The robot starts at the top-left corner (cell [0][0]
) and wants to reach the bottom-right corner (cell [m-1][n-1]
). The robot can only move either down or right at any point in time.
Your task is to determine how many unique paths exist from the start to the finish, considering the obstacles. If the starting or ending cell contains an obstacle, there are zero ways to reach the destination.
1
are blocked and cannot be entered.
At first glance, the problem seems similar to the classic "Unique Paths" problem, where you count the number of ways to go from the top-left to the bottom-right of a grid. However, the presence of obstacles complicates things, because you cannot pass through cells with a 1
.
A brute-force approach would be to try all possible paths recursively, but this quickly becomes infeasible for larger grids due to the exponential number of possibilities. We need to avoid recomputation and efficiently account for obstacles.
The key insight is that the number of ways to reach a cell is the sum of the ways to reach the cell above it and the cell to its left—unless that cell is blocked by an obstacle. This naturally leads to a dynamic programming (DP) approach, where we build up the solution by filling out a DP table row by row.
We'll use a 2D dynamic programming table dp
of the same size as obstacleGrid
, where dp[i][j]
represents the number of unique paths to cell (i, j)
.
obstacleGrid[0][0]
is 1
, there are zero paths. Otherwise, set dp[0][0] = 1
.(i, j)
in the grid:obstacleGrid[i][j]
is 1
, set dp[i][j] = 0
(blocked).dp[i][j] = dp[i-1][j] + dp[i][j-1]
, using 0
if out of bounds.dp[m-1][n-1]
gives the total unique paths from start to finish.This approach ensures that each subproblem is solved only once, and obstacles are handled naturally by setting their corresponding DP cells to zero.
Let's work through an example:
Input: obstacleGrid = [ [0,0,0], [0,1,0], [0,0,0] ]
dp[0][0] = 1
(since it's not blocked).dp[0][1] = 1
, dp[0][2] = 1
.dp[1][0] = 1
, dp[2][0] = 1
.dp[1][1]
, there's an obstacle, so dp[1][1] = 0
.dp[1][2]
: dp[1][1] + dp[0][2] = 0 + 1 = 1
.dp[2][1]
: dp[1][1] + dp[2][0] = 0 + 1 = 1
.dp[2][2]
: dp[1][2] + dp[2][1] = 1 + 1 = 2
.
The final answer is 2
, meaning there are two unique paths from the top-left to the bottom-right, avoiding the obstacle.
The DP approach is efficient and well-suited for large grids.
The "Unique Paths II" problem extends the classic grid path-counting challenge by adding obstacles. By using dynamic programming, we efficiently compute the number of unique paths from the start to the finish, handling obstacles by setting their DP values to zero. This approach avoids recomputation and leverages the overlapping subproblems inherent in the grid structure, resulting in a solution that is both elegant and performant.