There are 3 main approaches to solve this problem:
This problem is an extension of the "Unique Paths" problem, with the addition of obstacles. We can still use dynamic programming to solve it, but we need to handle the obstacles differently.
Let's define dp[i][j]
as the number of unique paths to reach the cell at position (i, j)
from the top-left corner (0, 0)
.
The key insight is that if a cell contains an obstacle (obstacleGrid[i][j] == 1
), then the number of ways to reach that cell is 0. For non-obstacle cells, the recurrence relation is the same as in "Unique Paths":
dp[i][j] = dp[i-1][j] + dp[i][j-1]
if obstacleGrid[i][j] == 0
dp[i][j] = 0
if obstacleGrid[i][j] == 1
For the base cases, we need to handle the first row and first column differently:
i = 0
), dp[0][j] = dp[0][j-1]
if obstacleGrid[0][j] == 0
, and dp[0][j] = 0
if obstacleGrid[0][j] == 1
.j = 0
), dp[i][0] = dp[i-1][0]
if obstacleGrid[i][0] == 0
, and dp[i][0] = 0
if obstacleGrid[i][0] == 1
.Additionally, we need to handle the starting cell (0, 0
) specially:
dp[0][0] = 1
if obstacleGrid[0][0] == 0
, and dp[0][0] = 0
if obstacleGrid[0][0] == 1
.The final answer is dp[m-1][n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We need to fill in the DP table of size m × n, and each cell takes constant time to compute.
We use a 2D DP array of size m × n to store the number of unique paths for each cell.
We can optimize the space complexity of the dynamic programming approach by using a 1D array instead of a 2D array, similar to the optimization in the "Unique Paths" problem.
The key observation is that when we're calculating dp[i][j]
, we only need the values from the current row and the row above it. Specifically, we need dp[i-1][j]
and dp[i][j-1]
.
If we use a 1D array dp
where dp[j]
represents the number of unique paths to reach the cell at position (i, j)
, then:
dp[j]
represents the number of unique paths to reach the cell at position (i-1, j)
.dp[j-1]
, dp[j-1]
represents the number of unique paths to reach the cell at position (i, j-1)
.So, the recurrence relation becomes:
dp[j] = dp[j] + dp[j-1]
if obstacleGrid[i][j] == 0
dp[j] = 0
if obstacleGrid[i][j] == 1
We initialize the 1D array based on the first row of the obstacle grid, and then update it row by row.
The final answer is dp[n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We still need to process each cell in the grid, which takes O(m * n) time.
We only use a 1D array of size n to store the number of unique paths for the current row.
We can further optimize the space complexity by using the input grid itself to store the DP values, thus avoiding the need for an additional array.
The key idea is to reuse the obstacleGrid
array to store the number of unique paths to reach each cell. We'll modify the array in-place:
obstacleGrid[i][j] == 1
(obstacle), we set obstacleGrid[i][j] = 0
(no paths).obstacleGrid[i][j] == 0
(no obstacle), we calculate the number of paths using the recurrence relation and store it in obstacleGrid[i][j]
.However, there's a complication: we need to distinguish between cells that are obstacles (which should have 0 paths) and cells that have 0 paths because they can't be reached. To handle this, we can first preprocess the grid to mark all obstacles as -1, and then apply the DP algorithm.
Alternatively, we can directly modify the grid without preprocessing, by carefully handling the base cases and the recurrence relation.
Note that this approach modifies the input grid, which might not be desirable in all situations. If preserving the input is important, we should use one of the previous approaches.
We still need to process each cell in the grid, which takes O(m * n) time.
We use the input grid itself to store the DP values, so we don't need any additional space beyond the input.
123456789101112131415161718192021222324252627282930313233343536function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Initialize DP array dp = new 2D array of size m × n, initialized with 0 // Set base case for starting cell if obstacleGrid[0][0] == 0: dp[0][0] = 1 else: dp[0][0] = 0 // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 0: dp[0][j] = dp[0][j-1] else: dp[0][j] = 0 // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 0: dp[i][0] = dp[i-1][0] else: dp[i][0] = 0 // Fill DP array for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 0: dp[i][j] = dp[i-1][j] + dp[i][j-1] else: dp[i][j] = 0 return dp[m-1][n-1]
Understand different approaches to solve the unique paths ii problem and analyze their efficiency.
This problem is an extension of the "Unique Paths" problem, with the addition of obstacles. We can still use dynamic programming to solve it, but we need to handle the obstacles differently.
Let's define dp[i][j]
as the number of unique paths to reach the cell at position (i, j)
from the top-left corner (0, 0)
.
The key insight is that if a cell contains an obstacle (obstacleGrid[i][j] == 1
), then the number of ways to reach that cell is 0. For non-obstacle cells, the recurrence relation is the same as in "Unique Paths":
dp[i][j] = dp[i-1][j] + dp[i][j-1]
if obstacleGrid[i][j] == 0
dp[i][j] = 0
if obstacleGrid[i][j] == 1
For the base cases, we need to handle the first row and first column differently:
i = 0
), dp[0][j] = dp[0][j-1]
if obstacleGrid[0][j] == 0
, and dp[0][j] = 0
if obstacleGrid[0][j] == 1
.j = 0
), dp[i][0] = dp[i-1][0]
if obstacleGrid[i][0] == 0
, and dp[i][0] = 0
if obstacleGrid[i][0] == 1
.Additionally, we need to handle the starting cell (0, 0
) specially:
dp[0][0] = 1
if obstacleGrid[0][0] == 0
, and dp[0][0] = 0
if obstacleGrid[0][0] == 1
.The final answer is dp[m-1][n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We can optimize the space complexity of the dynamic programming approach by using a 1D array instead of a 2D array, similar to the optimization in the "Unique Paths" problem.
The key observation is that when we're calculating dp[i][j]
, we only need the values from the current row and the row above it. Specifically, we need dp[i-1][j]
and dp[i][j-1]
.
If we use a 1D array dp
where dp[j]
represents the number of unique paths to reach the cell at position (i, j)
, then:
dp[j]
represents the number of unique paths to reach the cell at position (i-1, j)
.dp[j-1]
, dp[j-1]
represents the number of unique paths to reach the cell at position (i, j-1)
.So, the recurrence relation becomes:
dp[j] = dp[j] + dp[j-1]
if obstacleGrid[i][j] == 0
dp[j] = 0
if obstacleGrid[i][j] == 1
We initialize the 1D array based on the first row of the obstacle grid, and then update it row by row.
The final answer is dp[n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We can further optimize the space complexity by using the input grid itself to store the DP values, thus avoiding the need for an additional array.
The key idea is to reuse the obstacleGrid
array to store the number of unique paths to reach each cell. We'll modify the array in-place:
obstacleGrid[i][j] == 1
(obstacle), we set obstacleGrid[i][j] = 0
(no paths).obstacleGrid[i][j] == 0
(no obstacle), we calculate the number of paths using the recurrence relation and store it in obstacleGrid[i][j]
.However, there's a complication: we need to distinguish between cells that are obstacles (which should have 0 paths) and cells that have 0 paths because they can't be reached. To handle this, we can first preprocess the grid to mark all obstacles as -1, and then apply the DP algorithm.
Alternatively, we can directly modify the grid without preprocessing, by carefully handling the base cases and the recurrence relation.
Note that this approach modifies the input grid, which might not be desirable in all situations. If preserving the input is important, we should use one of the previous approaches.
We need to fill in the DP table of size m × n, and each cell takes constant time to compute.
We use a 2D DP array of size m × n to store the number of unique paths for each cell.
We still need to process each cell in the grid, which takes O(m * n) time.
We only use a 1D array of size n to store the number of unique paths for the current row.
We still need to process each cell in the grid, which takes O(m * n) time.
We use the input grid itself to store the DP values, so we don't need any additional space beyond the input.
123456789101112131415161718192021222324252627282930313233343536function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Initialize DP array dp = new 2D array of size m × n, initialized with 0 // Set base case for starting cell if obstacleGrid[0][0] == 0: dp[0][0] = 1 else: dp[0][0] = 0 // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 0: dp[0][j] = dp[0][j-1] else: dp[0][j] = 0 // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 0: dp[i][0] = dp[i-1][0] else: dp[i][0] = 0 // Fill DP array for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 0: dp[i][j] = dp[i-1][j] + dp[i][j-1] else: dp[i][j] = 0 return dp[m-1][n-1]
12345678910111213141516171819202122232425262728293031323334function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Initialize DP array dp = new 1D array of size n, initialized with 0 // Set base case for starting cell if obstacleGrid[0][0] == 0: dp[0] = 1 else: dp[0] = 0 // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 0: dp[j] = dp[j-1] else: dp[j] = 0 // Fill DP array for i from 1 to m-1: // Update first column if obstacleGrid[i][0] == 1: dp[0] = 0 // Update rest of the row for j from 1 to n-1: if obstacleGrid[i][j] == 1: dp[j] = 0 else: dp[j] = dp[j] + dp[j-1] return dp[n-1]
123456789101112131415161718192021222324252627282930function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Set base case for starting cell obstacleGrid[0][0] = 1 - obstacleGrid[0][0] // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 1: obstacleGrid[0][j] = 0 else: obstacleGrid[0][j] = obstacleGrid[0][j-1] // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 1: obstacleGrid[i][0] = 0 else: obstacleGrid[i][0] = obstacleGrid[i-1][0] // Fill grid for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 1: obstacleGrid[i][j] = 0 else: obstacleGrid[i][j] = obstacleGrid[i-1][j] + obstacleGrid[i][j-1] return obstacleGrid[m-1][n-1]
123456789101112131415161718192021222324252627282930function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Set base case for starting cell obstacleGrid[0][0] = 1 - obstacleGrid[0][0] // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 1: obstacleGrid[0][j] = 0 else: obstacleGrid[0][j] = obstacleGrid[0][j-1] // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 1: obstacleGrid[i][0] = 0 else: obstacleGrid[i][0] = obstacleGrid[i-1][0] // Fill grid for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 1: obstacleGrid[i][j] = 0 else: obstacleGrid[i][j] = obstacleGrid[i-1][j] + obstacleGrid[i][j-1] return obstacleGrid[m-1][n-1]
There are 3 main approaches to solve this problem:
This problem is an extension of the "Unique Paths" problem, with the addition of obstacles. We can still use dynamic programming to solve it, but we need to handle the obstacles differently.
Let's define dp[i][j]
as the number of unique paths to reach the cell at position (i, j)
from the top-left corner (0, 0)
.
The key insight is that if a cell contains an obstacle (obstacleGrid[i][j] == 1
), then the number of ways to reach that cell is 0. For non-obstacle cells, the recurrence relation is the same as in "Unique Paths":
dp[i][j] = dp[i-1][j] + dp[i][j-1]
if obstacleGrid[i][j] == 0
dp[i][j] = 0
if obstacleGrid[i][j] == 1
For the base cases, we need to handle the first row and first column differently:
i = 0
), dp[0][j] = dp[0][j-1]
if obstacleGrid[0][j] == 0
, and dp[0][j] = 0
if obstacleGrid[0][j] == 1
.j = 0
), dp[i][0] = dp[i-1][0]
if obstacleGrid[i][0] == 0
, and dp[i][0] = 0
if obstacleGrid[i][0] == 1
.Additionally, we need to handle the starting cell (0, 0
) specially:
dp[0][0] = 1
if obstacleGrid[0][0] == 0
, and dp[0][0] = 0
if obstacleGrid[0][0] == 1
.The final answer is dp[m-1][n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We need to fill in the DP table of size m × n, and each cell takes constant time to compute.
We use a 2D DP array of size m × n to store the number of unique paths for each cell.
We can optimize the space complexity of the dynamic programming approach by using a 1D array instead of a 2D array, similar to the optimization in the "Unique Paths" problem.
The key observation is that when we're calculating dp[i][j]
, we only need the values from the current row and the row above it. Specifically, we need dp[i-1][j]
and dp[i][j-1]
.
If we use a 1D array dp
where dp[j]
represents the number of unique paths to reach the cell at position (i, j)
, then:
dp[j]
represents the number of unique paths to reach the cell at position (i-1, j)
.dp[j-1]
, dp[j-1]
represents the number of unique paths to reach the cell at position (i, j-1)
.So, the recurrence relation becomes:
dp[j] = dp[j] + dp[j-1]
if obstacleGrid[i][j] == 0
dp[j] = 0
if obstacleGrid[i][j] == 1
We initialize the 1D array based on the first row of the obstacle grid, and then update it row by row.
The final answer is dp[n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We still need to process each cell in the grid, which takes O(m * n) time.
We only use a 1D array of size n to store the number of unique paths for the current row.
We can further optimize the space complexity by using the input grid itself to store the DP values, thus avoiding the need for an additional array.
The key idea is to reuse the obstacleGrid
array to store the number of unique paths to reach each cell. We'll modify the array in-place:
obstacleGrid[i][j] == 1
(obstacle), we set obstacleGrid[i][j] = 0
(no paths).obstacleGrid[i][j] == 0
(no obstacle), we calculate the number of paths using the recurrence relation and store it in obstacleGrid[i][j]
.However, there's a complication: we need to distinguish between cells that are obstacles (which should have 0 paths) and cells that have 0 paths because they can't be reached. To handle this, we can first preprocess the grid to mark all obstacles as -1, and then apply the DP algorithm.
Alternatively, we can directly modify the grid without preprocessing, by carefully handling the base cases and the recurrence relation.
Note that this approach modifies the input grid, which might not be desirable in all situations. If preserving the input is important, we should use one of the previous approaches.
We still need to process each cell in the grid, which takes O(m * n) time.
We use the input grid itself to store the DP values, so we don't need any additional space beyond the input.
123456789101112131415161718192021222324252627282930313233343536function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Initialize DP array dp = new 2D array of size m × n, initialized with 0 // Set base case for starting cell if obstacleGrid[0][0] == 0: dp[0][0] = 1 else: dp[0][0] = 0 // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 0: dp[0][j] = dp[0][j-1] else: dp[0][j] = 0 // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 0: dp[i][0] = dp[i-1][0] else: dp[i][0] = 0 // Fill DP array for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 0: dp[i][j] = dp[i-1][j] + dp[i][j-1] else: dp[i][j] = 0 return dp[m-1][n-1]
Understand different approaches to solve the unique paths ii problem and analyze their efficiency.
This problem is an extension of the "Unique Paths" problem, with the addition of obstacles. We can still use dynamic programming to solve it, but we need to handle the obstacles differently.
Let's define dp[i][j]
as the number of unique paths to reach the cell at position (i, j)
from the top-left corner (0, 0)
.
The key insight is that if a cell contains an obstacle (obstacleGrid[i][j] == 1
), then the number of ways to reach that cell is 0. For non-obstacle cells, the recurrence relation is the same as in "Unique Paths":
dp[i][j] = dp[i-1][j] + dp[i][j-1]
if obstacleGrid[i][j] == 0
dp[i][j] = 0
if obstacleGrid[i][j] == 1
For the base cases, we need to handle the first row and first column differently:
i = 0
), dp[0][j] = dp[0][j-1]
if obstacleGrid[0][j] == 0
, and dp[0][j] = 0
if obstacleGrid[0][j] == 1
.j = 0
), dp[i][0] = dp[i-1][0]
if obstacleGrid[i][0] == 0
, and dp[i][0] = 0
if obstacleGrid[i][0] == 1
.Additionally, we need to handle the starting cell (0, 0
) specially:
dp[0][0] = 1
if obstacleGrid[0][0] == 0
, and dp[0][0] = 0
if obstacleGrid[0][0] == 1
.The final answer is dp[m-1][n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We can optimize the space complexity of the dynamic programming approach by using a 1D array instead of a 2D array, similar to the optimization in the "Unique Paths" problem.
The key observation is that when we're calculating dp[i][j]
, we only need the values from the current row and the row above it. Specifically, we need dp[i-1][j]
and dp[i][j-1]
.
If we use a 1D array dp
where dp[j]
represents the number of unique paths to reach the cell at position (i, j)
, then:
dp[j]
represents the number of unique paths to reach the cell at position (i-1, j)
.dp[j-1]
, dp[j-1]
represents the number of unique paths to reach the cell at position (i, j-1)
.So, the recurrence relation becomes:
dp[j] = dp[j] + dp[j-1]
if obstacleGrid[i][j] == 0
dp[j] = 0
if obstacleGrid[i][j] == 1
We initialize the 1D array based on the first row of the obstacle grid, and then update it row by row.
The final answer is dp[n-1]
, which represents the number of unique paths to reach the bottom-right corner.
We can further optimize the space complexity by using the input grid itself to store the DP values, thus avoiding the need for an additional array.
The key idea is to reuse the obstacleGrid
array to store the number of unique paths to reach each cell. We'll modify the array in-place:
obstacleGrid[i][j] == 1
(obstacle), we set obstacleGrid[i][j] = 0
(no paths).obstacleGrid[i][j] == 0
(no obstacle), we calculate the number of paths using the recurrence relation and store it in obstacleGrid[i][j]
.However, there's a complication: we need to distinguish between cells that are obstacles (which should have 0 paths) and cells that have 0 paths because they can't be reached. To handle this, we can first preprocess the grid to mark all obstacles as -1, and then apply the DP algorithm.
Alternatively, we can directly modify the grid without preprocessing, by carefully handling the base cases and the recurrence relation.
Note that this approach modifies the input grid, which might not be desirable in all situations. If preserving the input is important, we should use one of the previous approaches.
We need to fill in the DP table of size m × n, and each cell takes constant time to compute.
We use a 2D DP array of size m × n to store the number of unique paths for each cell.
We still need to process each cell in the grid, which takes O(m * n) time.
We only use a 1D array of size n to store the number of unique paths for the current row.
We still need to process each cell in the grid, which takes O(m * n) time.
We use the input grid itself to store the DP values, so we don't need any additional space beyond the input.
123456789101112131415161718192021222324252627282930313233343536function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Initialize DP array dp = new 2D array of size m × n, initialized with 0 // Set base case for starting cell if obstacleGrid[0][0] == 0: dp[0][0] = 1 else: dp[0][0] = 0 // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 0: dp[0][j] = dp[0][j-1] else: dp[0][j] = 0 // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 0: dp[i][0] = dp[i-1][0] else: dp[i][0] = 0 // Fill DP array for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 0: dp[i][j] = dp[i-1][j] + dp[i][j-1] else: dp[i][j] = 0 return dp[m-1][n-1]
12345678910111213141516171819202122232425262728293031323334function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Initialize DP array dp = new 1D array of size n, initialized with 0 // Set base case for starting cell if obstacleGrid[0][0] == 0: dp[0] = 1 else: dp[0] = 0 // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 0: dp[j] = dp[j-1] else: dp[j] = 0 // Fill DP array for i from 1 to m-1: // Update first column if obstacleGrid[i][0] == 1: dp[0] = 0 // Update rest of the row for j from 1 to n-1: if obstacleGrid[i][j] == 1: dp[j] = 0 else: dp[j] = dp[j] + dp[j-1] return dp[n-1]
123456789101112131415161718192021222324252627282930function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Set base case for starting cell obstacleGrid[0][0] = 1 - obstacleGrid[0][0] // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 1: obstacleGrid[0][j] = 0 else: obstacleGrid[0][j] = obstacleGrid[0][j-1] // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 1: obstacleGrid[i][0] = 0 else: obstacleGrid[i][0] = obstacleGrid[i-1][0] // Fill grid for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 1: obstacleGrid[i][j] = 0 else: obstacleGrid[i][j] = obstacleGrid[i-1][j] + obstacleGrid[i][j-1] return obstacleGrid[m-1][n-1]
123456789101112131415161718192021222324252627282930function uniquePathsWithObstacles(obstacleGrid): m = obstacleGrid.length n = obstacleGrid[0].length // Set base case for starting cell obstacleGrid[0][0] = 1 - obstacleGrid[0][0] // Set base cases for first row for j from 1 to n-1: if obstacleGrid[0][j] == 1: obstacleGrid[0][j] = 0 else: obstacleGrid[0][j] = obstacleGrid[0][j-1] // Set base cases for first column for i from 1 to m-1: if obstacleGrid[i][0] == 1: obstacleGrid[i][0] = 0 else: obstacleGrid[i][0] = obstacleGrid[i-1][0] // Fill grid for i from 1 to m-1: for j from 1 to n-1: if obstacleGrid[i][j] == 1: obstacleGrid[i][j] = 0 else: obstacleGrid[i][j] = obstacleGrid[i-1][j] + obstacleGrid[i][j-1] return obstacleGrid[m-1][n-1]