You are given an m x n integer array grid. There is a robot initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time.
An obstacle and space are marked as 1 or 0 respectively in grid. A path that the robot takes cannot include any square that is an obstacle.
Return the number of possible unique paths that the robot can take to reach the bottom-right corner.
The testcases are generated so that the answer will be less than or equal to 2 * 109.
Example 1:
Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
Output: 2
Explanation: There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
Example 2:
Input: obstacleGrid = [[0,1],[0,0]]
Output: 1
Constraints:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j] is 0 or 1.
Solutions
Solution 1: Memoization Search
Thinking
As in Unique Paths, a raw search that only moves down or right explodes: \(m, n \le 100\). Obstacles also break the “first row and column are all \(1\)” shortcut.
The bottleneck is expanding the same cell \((i, j)\) many times. From here, out-of-bounds or an obstacle is \(0\), the destination is \(1\), otherwise the two branches add.
Memoize \(\textit{dfs}(i, j)\) so each cell is computed once; time and space are both \(O(mn)\).
We design a function \(\textit{dfs}(i, j)\) to represent the number of paths from the grid \((i, j)\) to the grid \((m - 1, n - 1)\). Here, \(m\) and \(n\) are the number of rows and columns of the grid, respectively.
The execution process of the function \(\textit{dfs}(i, j)\) is as follows:
If \(i \ge m\) or \(j \ge n\), or \(\textit{obstacleGrid}[i][j] = 1\), the number of paths is \(0\);
If \(i = m - 1\) and \(j = n - 1\), the number of paths is \(1\);
Otherwise, the number of paths is \(\textit{dfs}(i + 1, j) + \textit{dfs}(i, j + 1)\).
To avoid redundant calculations, we can use memoization.
The time complexity is \(O(m \times n)\), and the space complexity is \(O(m \times n)\). Here, \(m\) and \(n\) are the number of rows and columns of the grid, respectively.
Solution 1 is already \(O(mn)\) after memoization, but it is still recursive, with a deeper implicit stack and larger constants. Obstacles cut the first row and column, so those borders are no longer all \(1\); filling a table bottom-up is more direct.
\(f[i][j]\) is the number of paths from the start: \(0\) on an obstacle, otherwise from above and the left. Prefill the first row and column up to the first obstacle, then fill the interior—no recursion.
We can use a dynamic programming approach by defining a 2D array \(f\), where \(f[i][j]\) represents the number of paths from the grid \((0,0)\) to the grid \((i,j)\).
We first initialize all values in the first column and the first row of \(f\), then traverse the other rows and columns with two cases:
If \(\textit{obstacleGrid}[i][j] = 1\), it means the number of paths is \(0\), so \(f[i][j] = 0\);
If \(\textit{obstacleGrid}[i][j] = 0\), then \(f[i][j] = f[i - 1][j] + f[i][j - 1]\).
Finally, return \(f[m - 1][n - 1]\).
The time complexity is \(O(m \times n)\), and the space complexity is \(O(m \times n)\). Here, \(m\) and \(n\) are the number of rows and columns of the grid, respectively.