Related: Unique Paths

Intuition

  • This problem is the same with the Memoization format, but you simply choose not to go down the DFS paths that have an obstacle (return 0 when out of bounds or when you reach an obstacle).
  • Note that the difference between this problem and Unique Paths is you have to be careful about pre-initializing the array, since if there is a blocker on the last row or last column, then the previous values are also blocked, since you can only go down on the last column and can only go to the right on the last row.
  • So with the 1D DP method, we do the same thing by setting that DP index to 0, thereby making that entire set of subproblems handled.

Runtime

  • Same as Unique Paths, with runtime and space with the 1D DP version.

Code

1D DP

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        M, N = len(obstacleGrid), len(obstacleGrid[0])
        dp = [0] * N
        dp[-1] = 1
        for r in reversed(range(M)):
            for c in reversed(range(N)):
                if obstacleGrid[r][c] == 1:
                    dp[c] = 0
                elif c < N - 1:
                    dp[c] = dp[c] + dp[c + 1]
 
        return dp[0]

Tabulation DP

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        if obstacleGrid[-1][-1] == 1:
            return 0
            
        m, n = len(obstacleGrid), len(obstacleGrid[0])
       
        nums = []
        for i in range(m - 1):
            nums.append([0] * (n - 1) + [1])
        nums.append([1] * n)
 
        is_blocked = False
        for i in range(n - 1, -1, -1):
            if obstacleGrid[-1][i] == 1:
                is_blocked = True
            if is_blocked:
                nums[-1][i] = 0
                
        is_blocked = False
        for j in range(m - 1, -1, -1):
            if obstacleGrid[j][-1] == 1:
                is_blocked = True
            if is_blocked:
                nums[j][-1] = 0
 
        for i in range(m - 2, -1, -1):
            for j in range(n - 2, -1, -1):
                if obstacleGrid[i][j] == 1:
                    nums[i][j] = 0
                else:
                    nums[i][j] = nums[i + 1][j] + nums[i][j + 1]
        
        return nums[0][0]

Memoization

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        dp = {}
        def dfs(row, col):
            if row >= m or col >= n or obstacleGrid[row][col] == 1:
                return 0
            if row == m - 1 and col == n - 1:
                return 1
            if (row, col) in dp:
                return dp[(row, col)]
            dp[(row, col)] = dfs(row + 1, col) + dfs(row, col + 1)
            return dp[(row, col)]
        
        return dfs(0, 0)

Notes


References