Loading problems...
TL;DR: The optimal solution uses a 2D Dynamic Programming approach to calculate the number of paths to each cell by summing the valid paths from the cell directly above and the cell directly to the left, while treating obstacles as having zero paths.
In LeetCode 63: Unique Paths II, we are tasked with finding the number of distinct paths a robot can take to travel from the top-left corner to the bottom-right corner of a grid. The robot is constrained to move only down or right. Unlike the standard Unique Paths problem, this grid contains obstacles marked as 1, which the robot cannot traverse. We must return the total count of unique paths avoiding these obstacles.
The brute force strategy attempts to explore every possible valid path from the start to the destination using recursion. This is typically implemented as a Depth First Search (DFS).
At every cell , the algorithm recursively calls itself for the next possible moves: (down) and (right), provided those cells are within bounds and are not obstacles. If the robot reaches the bottom-right corner, the function returns 1 (indicating a successful path). If it hits a boundary or an obstacle, it returns 0.
textfunction countPaths(r, c): if r or c is out of bounds or grid[r][c] is obstacle: return 0 if r is destination_row and c is destination_col: return 1 return countPaths(r + 1, c) + countPaths(r, c + 1)
The time complexity of this approach is exponential, specifically . This occurs because the recursion tree re-calculates the number of paths for the same cells repeatedly. For a grid of size , the number of operations far exceeds the limits of modern processors, resulting in a Time Limit Exceeded (TLE) error.
The transition from the brute force approach to the optimal solution relies on the observation that the robot can only reach cell from two specific directions:
Therefore, the total number of unique paths to reach is strictly the sum of the unique paths to reach and the unique paths to reach .
The obstacle constraint introduces a simple condition: if a cell contains an obstacle, no path can flow through it. Mathematically, this means the number of paths to an obstacle cell is 0. This "zeroing out" effect propagates to subsequent cells; if a path is blocked, it contributes nothing to the sum of its downstream neighbors.
Visual Description: Imagine a 2D table mirroring the grid dimensions. We process this table row by row. For any given cell, we look at the value in the cell above it and the value in the cell to the left of it in our table. We sum these two values to determine the current cell's value. If the current cell corresponds to an obstacle in the input grid, we force the value to 0, effectively cutting off the flow of paths through that coordinate.

dp[i][j] represent the number of unique paths from the start to the cell .dp of size .obstacleGrid[0][0] is 1, return 0 immediately (no paths possible). Otherwise, set dp[0][0] = 1.obstacleGrid[i][j] == 1, set dp[i][j] = 0.dp[i][j] = dp[i-1][j] + dp[i][j-1].dp[m-1][n-1] contains the total unique paths to the destination.obstacleGrid. Let be rows and be columns.obstacleGrid[0][0] is 1, the robot cannot start. Return 0.dp[m][n] with zeros. Set dp[0][0] = 1.obstacleGrid[i][0] is 0, dp[i][0] = dp[i-1][0]. If it is 1, dp[i][0] remains 0 (and all subsequent cells in this column remain 0).obstacleGrid[0][j] is 0, dp[0][j] = dp[0][j-1]. If it is 1, dp[0][j] remains 0.dp[m-1][n-1].The correctness is proven via induction.
dp[r-1][c] correctly stores paths to the top neighbor and dp[r][c-1] correctly stores paths to the left neighbor. Since the robot can only arrive at from these two positions, the sum of paths to these neighbors represents the exhaustive set of paths to . The obstacle check ensures invalid paths are excluded (adding 0).cppclass Solution { public: int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) { int m = obstacleGrid.size(); int n = obstacleGrid[0].size(); // If the starting cell is an obstacle, no paths exist. if (obstacleGrid[0][0] == 1) { return 0; } // DP table initialization vector<vector<int>> dp(m, vector<int>(n, 0)); // Base case dp[0][0] = 1; // Fill the first column for (int i = 1; i < m; i++) { if (obstacleGrid[i][0] == 0) { dp[i][0] = dp[i-1][0]; } else { dp[i][0] = 0; } } // Fill the first row for (int j = 1; j < n; j++) { if (obstacleGrid[0][j] == 0) { dp[0][j] = dp[0][j-1]; } else { dp[0][j] = 0; } } // Fill the rest of the grid 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]; } };
javaclass Solution { public int uniquePathsWithObstacles(int[][] obstacleGrid) { int m = obstacleGrid.length; int n = obstacleGrid[0].length; // If start is blocked, return 0 if (obstacleGrid[0][0] == 1) { return 0; } int[][] dp = new int[m][n]; dp[0][0] = 1; // Initialize first column for (int i = 1; i < m; i++) { if (obstacleGrid[i][0] == 0) { dp[i][0] = dp[i - 1][0]; } else { dp[i][0] = 0; } } // Initialize first row for (int j = 1; j < n; j++) { if (obstacleGrid[0][j] == 0) { dp[0][j] = dp[0][j - 1]; } else { dp[0][j] = 0; } } // Fill DP table 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]; } }
pythonclass Solution: def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: m = len(obstacleGrid) n = len(obstacleGrid[0]) # If starting cell is an obstacle, return 0 if obstacleGrid[0][0] == 1: return 0 dp = [[0] * n for _ in range(m)] dp[0][0] = 1 # Initialize first column for i in range(1, m): if obstacleGrid[i][0] == 0: dp[i][0] = dp[i-1][0] else: dp[i][0] = 0 # Initialize first row for j in range(1, n): if obstacleGrid[0][j] == 0: dp[0][j] = dp[0][j-1] else: dp[0][j] = 0 # Fill DP table for i in range(1, m): for j in range(1, n): 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]
The DP - 2D Array pattern used in LeetCode 63 is fundamental for grid-based optimization problems. The same logic applies to:
obstacleGrid[0][0] immediately.1s (like in Unique Paths I) without checking for obstacles.grid[0][2] is an obstacle, grid[0][3] becomes unreachable.obstacleGrid[i][j] is 1, dp[i][j] = 0.dp[i][j] = dp[i-1][j] + dp[i][j-1].