hard Shortest Path in a Grid with Obstacles Elimination
Problem Statement
You are given an m x n binary matrix grid where each cell is either empty (0) or has an obstacle (1). You can move up, down, left, or right, but only through empty cells. You can remove up to k obstacles along the way.
Return the shortest path from the top-left corner (0, 0) to the bottom-right corner (m - 1, n - 1). If it's not possible to reach the end, return -1.
Examples
Example 1
- Input: k = 1, grid =
[[0, 1, 0, 0],
[1, 1, 0, 1],
[0, 0, 0, 0],
[0, 1, 1, 0]]
-
Expected Output:
6 -
Justification: You can remove one obstacle to create a path from (0, 0) to (3, 3). The path can be (0,0) → (0,1) → (0,2) → (1,2) → (2,2) → (2,3) → (3,3). Here, we have removed the obstacle from (0, 1) position.
Example 2
- Input: k = 2, grid =
[[0, 0, 0],
[1, 1, 0],
[1, 1, 0],
[0, 0, 0]]
- Expected Output:
5 - Justification: You can remove two obstacles to create a path from (0, 0) to (3, 2). The path can be (0,0) → (0,1) → (0,2) → (1,2) → (2,2) -> (3, 2). Here, we don't need to remove any obstacles.
Example 3
- Input: k = 3, grid =
[[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]]
-
Expected Output:
5 -
Justification: You can remove three obstacles to create a path from (0, 0) to (3, 2). The path can be (0,0) → (0,1) → (0,2) → (1,2) → (2,2) → (3,2). Here, we have removed the obstacle from (0, 2) position.
Constraints:
- m == grid.length
- n == grid[i].length
- 1 <= m, n <= 40
- 1 <= k <= m * n
- grid[i][j] is either 0 or 1.
- grid[0][0] == grid[m - 1][n - 1] == 0
Try it yourself
Try solving this question here:
✅ Solution Shortest Path in a Grid with Obstacles Elimination
Problem Statement
You are given an m x n binary matrix grid where each cell is either empty (0) or has an obstacle (1). You can move up, down, left, or right, but only through empty cells. You can remove up to k obstacles along the way.
Return the shortest path from the top-left corner (0, 0) to the bottom-right corner (m - 1, n - 1). If it's not possible to reach the end, return -1.
Examples
Example 1
- Input: k = 1, grid =
[[0, 1, 0, 0],
[1, 1, 0, 1],
[0, 0, 0, 0],
[0, 1, 1, 0]]
-
Expected Output:
6 -
Justification: You can remove one obstacle to create a path from (0, 0) to (3, 3). The path can be (0,0) → (0,1) → (0,2) → (1,2) → (2,2) → (2,3) → (3,3). Here, we have removed the obstacle from (0, 1) position.
Example 2
- Input: k = 2, grid =
[[0, 0, 0],
[1, 1, 0],
[1, 1, 0],
[0, 0, 0]]
- Expected Output:
5 - Justification: You can remove two obstacles to create a path from (0, 0) to (3, 2). The path can be (0,0) → (0,1) → (0,2) → (1,2) → (2,2) -> (3, 2). Here, we don't need to remove any obstacles.
Example 3
- Input: k = 3, grid =
[[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]]
-
Expected Output:
5 -
Justification: You can remove three obstacles to create a path from (0, 0) to (3, 2). The path can be (0,0) → (0,1) → (0,2) → (1,2) → (2,2) → (3,2). Here, we have removed the obstacle from (0, 2) position.
Constraints:
- m == grid.length
- n == grid[i].length
- 1 <= m, n <= 40
- 1 <= k <= m * n
- grid[i][j] is either 0 or 1.
- grid[0][0] == grid[m - 1][n - 1] == 0
Solution
To solve this problem, we use a modified Breadth-First Search (BFS) approach. This method is efficient for finding the shortest path in an unweighted grid. By keeping track of the number of obstacles we can remove, we ensure that we explore all potential paths. This approach works well because BFS explores all nodes at the present depth level before moving on to nodes at the next depth level, ensuring the shortest path is found.
The key idea is to use a queue to store the current position, the number of steps taken, and the number of obstacles that can still be removed. We also use a set to keep track of visited positions along with the remaining obstacle removal quota to prevent redundant exploration.
Step-by-step Algorithm
-
Initialization:
- Create a queue to store the state as a tuple
(row, col, steps, remaining_k), representing the current position, steps taken so far, and remaining obstacle removals. - Initialize the queue with the starting point
(0, 0, 0, k)(top-left corner with 0 steps taken and k obstacle removals). - Create a 3D boolean array
visitedof sizem x n x (k + 1)to keep track of visited states, wherevisited[row][col][remaining_k]is true if the state(row, col, remaining_k)has been visited. - Mark the starting point as visited:
visited[0][0][k] = true.
- Create a queue to store the state as a tuple
-
BFS Loop:
- While the queue is not empty:
- Dequeue the front element to get the current state
(x, y, steps, remaining_k). - Check if the current position
(x, y)is the bottom-right corner(m-1, n-1). If yes, return the number of steps. - For each of the four possible movements (up, down, left, right):
- Calculate the new position
(new_x, new_y). - Check if the new position is within grid bounds.
- Calculate the new remaining obstacle removals:
new_k = remaining_k - grid[new_x][new_y]. - If
new_kis non-negative and the state(new_x, new_y, new_k)has not been visited:- Mark the new state as visited:
visited[new_x][new_y][new_k] = true. - Enqueue the new state
(new_x, new_y, steps + 1, new_k).
- Mark the new state as visited:
- Calculate the new position
- Dequeue the front element to get the current state
- While the queue is not empty:
-
End Condition:
- If the queue is empty and the bottom-right corner is not reached, return
-1.
- If the queue is empty and the bottom-right corner is not reached, return
Algorithm Walkthrough
For the given input k = 3, grid =
[[0, 0, 1],
[0, 1, 0],
[1, 0, 0],
[0, 1, 0]]
-
Initialization:
queue = deque([(0, 0, 0, 3)])visited = [[[False] * 4 for _ in range(3)] for _ in range(4)]visited[0][0][3] = True
-
BFS Loop:
-
Iteration 1:
- Dequeue
(0, 0, 0, 3), explore its neighbors:- Move to
(0, 1)(right): new state(0, 1, 1, 3), mark visited and enqueue. - Move to
(1, 0)(down): new state(1, 0, 1, 3), mark visited and enqueue. - No valid move to
(0, -1)(left) or(-1, 0)(up).
- Move to
queue = deque([(0, 1, 1, 3), (1, 0, 1, 3)])
- Dequeue
-
Iteration 2:
- Dequeue
(0, 1, 1, 3), explore its neighbors:- Move to
(0, 2)(right): new state(0, 2, 2, 2)(obstacle removed), mark visited and enqueue. - Move to
(1, 1)(down): new state(1, 1, 2, 2)(obstacle removed), mark visited and enqueue. - No valid move to
(0, 0)(left) (already visited) or(-1, 1)(up).
- Move to
queue = deque([(1, 0, 1, 3), (0, 2, 2, 2), (1, 1, 2, 2)])
- Dequeue
-
Iteration 3:
- Dequeue
(1, 0, 1, 3), explore its neighbors:- Move to
(1, 1)(right): new state(1, 1, 2, 2)(already visited). - Move to
(2, 0)(down): new state(2, 0, 2, 2)(obstacle removed), mark visited and enqueue. - Move to
(1, -1)(left): invalid move (out of bounds). - Move to
(0, 0)(up): new state(0, 0, 2, 3)(already visited).
- Move to
queue = deque([(0, 2, 2, 2), (1, 1, 2, 2), (2, 0, 2, 2)])
- Dequeue
-
Iteration 4:
- Dequeue
(0, 2, 2, 2), explore its neighbors:- Move to
(0, 3)(right): invalid move (out of bounds). - Move to
(1, 2)(down): new state(1, 2, 3, 2), mark visited and enqueue. - Move to
(0, 1)(left): new state(0, 1, 3, 2)(already visited). - Move to
(-1, 2)(up): invalid move (out of bounds).
- Move to
queue = deque([(1, 1, 2, 2), (2, 0, 2, 2), (1, 2, 3, 2)])
- Dequeue
-
Iteration 5:
- Dequeue
(1, 1, 2, 2), explore its neighbors:- Move to
(1, 2)(right): new state(1, 2, 3, 2)(already visited). - Move to
(2, 1)(down): new state(2, 1, 3, 2), mark visited and enqueue. - Move to
(1, 0)(left): new state(1, 0, 3, 2)(already visited). - Move to
(0, 1)(up): new state(0, 1, 3, 2)(already visited).
- Move to
queue = deque([(2, 0, 2, 2), (1, 2, 3, 2), (2, 1, 3, 2)])
- Dequeue
-
Iteration 6:
- Dequeue
(2, 0, 2, 2), explore its neighbors:- Move to
(2, 1)(right): new state(2, 1, 3, 2)(already visited). - Move to
(3, 0)(down): new state(3, 0, 3, 2), mark visited and enqueue. - Move to
(2, -1)(left): invalid move (out of bounds). - Move to
(1, 0)(up): new state(1, 0, 3, 2)(already visited).
- Move to
queue = deque([(1, 2, 3, 2), (2, 1, 3, 2), (3, 0, 3, 2)])
- Dequeue
-
Iteration 7:
- Dequeue
(1, 2, 3, 2), explore its neighbors:- Move to
(1, 3)(right): invalid move (out of bounds). - Move to
(2, 2)(down): new state(2, 2, 4, 2), mark visited and enqueue. - Move to
(1, 1)(left): new state(1, 1, 4, 2)(already visited). - Move to
(0, 2)(up): new state(0, 2, 4, 2)(already visited).
- Move to
queue = deque([(2, 1, 3, 2), (3, 0, 3, 2), (2, 2, 4, 2)])
- Dequeue
-
Iteration 8:
- Dequeue
(2, 1, 3, 2), explore its neighbors:- Move to
(2, 2)(right): new state(2, 2, 4, 2)(already visited). - Move to
(3, 1)(down): new state(3, 1, 4, 1)(obstacle removed), mark visited and enqueue. - Move to
(2, 0)(left): new state(2, 0, 4, 2)(already visited). - Move to
(1, 1)(up): new state(1, 1, 4, 2)(already visited).
- Move to
queue = deque([(3, 0, 3, 2), (2, 2, 4, 2), (3, 1, 4, 1)])
- Dequeue
-
Iteration 9:
- Dequeue
(3, 0, 3, 2), explore its neighbors:- Move to
(3, 1)(right): new state(3, 1, 4, 1)(already visited). - Move to
(4, 0)(down): invalid move (out of bounds). - Move to
(3, -1)(left): invalid move (out of bounds). - Move to
(2, 0)(up): new state(2, 0, 4, 2)(already visited).
- Move to
queue = deque([(2, 2, 4, 2), (3, 1, 4, 1)])
- Dequeue
-
Iteration 10:
- Dequeue
(2, 2, 4, 2), explore its neighbors:- Move to
(2, 3)(right): invalid move (out of bounds). - Move to
(3, 2)(down): new state(3, 2, 5, 2), mark visited and enqueue. - Move to
(2, 1)(left): new state(2, 1, 5, 2)(already visited). - Move to
(1, 2)(up): new state(1, 2, 5, 2)(already visited).
- Move to
queue = deque([(3, 1, 4, 1), (3, 2, 5, 2)])
- Dequeue
-
Iteration 11:
- Dequeue
(3, 1, 4, 1), explore its neighbors:- Move to
(3, 2)(right): new state(3, 2, 5, 1)(already visited). - Move to
(4, 1)(down): invalid move (out of bounds). - Move to
(3, 0)(left): new state(3, 0, 5, 1)(already visited). - Move to
(2, 1)(up): new state(2, 1, 5, 1)(already visited).
- Move to
queue = deque([(3, 2, 5, 2)])
- Dequeue
-
Iteration 12:
- Dequeue
(3, 2, 5, 2), explore its neighbors:- Move to
(3, 3)(right): invalid move (out of bounds). - Move to
(4, 2)(down): invalid move (out of bounds). - Move to
(3, 1)(left): new state(3, 1, 6, 2)(already visited). - Move to
(2, 2)(up): new state(2, 2, 6, 2)(already visited).
- Move to
queue = deque([])
- Dequeue
-
Since the queue is empty and the bottom-right corner is not reached, return
-1.
-
-
End Condition:
- The bottom-right corner
(3, 2)is reached in5steps with2obstacles removed.
- The bottom-right corner
So, the expected output is 5.
Code
import java.util.*;
class Solution {
public int shortestPath(int[][] grid, int k) {
int m = grid.length;
int n = grid[0].length;
// Initialize the queue for BFS
Queue<int[]> queue = new LinkedList<>();
// Add starting point with 0 steps and k obstacle removals left
queue.offer(new int[] { 0, 0, 0, k });
// 3D array to keep track of visited states [row][col][remaining k]
boolean[][][] visited = new boolean[m][n][k + 1];
// Mark the starting point as visited
visited[0][0][k] = true;
// Directions array for moving up, down, left, and right
int[][] directions = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
// BFS loop
while (!queue.isEmpty()) {
// Dequeue the front element
int[] current = queue.poll();
int x = current[0];
int y = current[1];
int steps = current[2];
int remainingK = current[3];
// Check if we have reached the bottom-right corner
if (x == m - 1 && y == n - 1) {
return steps;
}
// Explore all four possible directions
for (int[] dir : directions) {
int newX = x + dir[0];
int newY = y + dir[1];
// Check if new position is within grid bounds
if (newX >= 0 && newX < m && newY >= 0 && newY < n) {
// Calculate remaining k after moving to new cell
int newK = remainingK - grid[newX][newY];
// If the new state is valid and not visited
if (newK >= 0 && !visited[newX][newY][newK]) {
// Mark new state as visited
visited[newX][newY][newK] = true;
// Enqueue the new state
queue.offer(new int[] { newX, newY, steps + 1, newK });
}
}
}
}
// Return -1 if no path is found
return -1;
}
public static void main(String[] args) {
Solution sol = new Solution();
int[][] grid1 = {
{ 0, 1, 0, 0 },
{ 1, 1, 0, 1 },
{ 0, 0, 0, 0 },
{ 0, 1, 1, 0 },
};
int k1 = 1;
System.out.println(sol.shortestPath(grid1, k1)); // Expected: 6
int[][] grid2 = { { 0, 0, 0 }, { 1, 1, 0 }, { 1, 1, 0 }, { 0, 0, 0 } };
int k2 = 2;
System.out.println(sol.shortestPath(grid2, k2)); // Expected: 5
int[][] grid3 = { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 }, { 0, 1, 0 } };
int k3 = 3;
System.out.println(sol.shortestPath(grid3, k3)); // Expected: 5
}
}
Complexity Analysis
Time Complexity
- In the worst-case scenario, we might explore every cell in the grid for every possible value of remaining k (from k down to 0).
- This results in a time complexity of
, where m is the number of rows and n is the number of columns in the grid. - Each cell can be visited in up to k different states (each state representing a different number of obstacles that can still be removed).
Space Complexity
- The space complexity is also
due to the visited array, which keeps track of the state of each cell for each possible value of remaining k. - Additionally, the queue used for BFS can hold up to m * n * k elements in the worst case.
🤖 Don't fully get this? Learn it with Claude
Stuck on Shortest Path in a Grid with Obstacles Elimination? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Progressively stronger hints — you still solve it.
I'm working on the problem **Shortest Path in a Grid with Obstacles Elimination** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
See the technique, not just code.
Explain the optimal approach to **Shortest Path in a Grid with Obstacles Elimination** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Catch bugs, edge cases, sub-optimality.
I'll paste my solution to **Shortest Path in a Grid with Obstacles Elimination**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Lock in recognition with look-alikes.
Give me 2 problems that use the SAME underlying pattern as **Shortest Path in a Grid with Obstacles Elimination**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.