Shortest Path in a Grid with Obstacles Elimination — Hard Problem & Solution
grid is an m x n matrix where 0 is an empty cell and 1 is an obstacle. In one step you move up, down, left or right to an adjacent cell.
- Difficulty: Hard
- Topics: Arrays, Matrix, Breadth-First Search
- Asked at: Amazon, Google, Meta
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
grid is an m x n matrix where 0 is an empty cell and 1 is an obstacle. In one step you move up, down, left or right to an adjacent cell.
You start in the top-left corner (0, 0) and want to reach the bottom-right corner (m - 1, n - 1). Along the way you may eliminate at most k obstacles, which lets you step onto those obstacle cells.
Return the minimum number of steps needed, or -1 if the corner cannot be reached with at most k eliminations.
Example 1
Input: grid = [[0,1,1],[1,1,0],[1,0,0]], k = 1
Output: -1
Explanation: Every route from the start crosses at least two obstacles.
Example 2
Input: grid = [[0,1,1],[1,1,0],[1,0,0]], k = 2
Output: 4
Explanation: Right through an obstacle, down through another, then down and right.
Example 3
Input: grid = [[0,0,0,0],[1,1,1,0],[0,0,0,0],[0,1,1,1],[0,0,0,0]], k = 1
Output: 7
Explanation: Without eliminations the path snakes for 13 steps; removing the obstacle at `(1,0)` allows the direct 7-step route.
Constraints
m == grid.lengthn == grid[i].length1 <= m, n <= 401 <= k <= m * ngrid[i][j] is 0 or 1grid[0][0] == grid[m - 1][n - 1] == 0
How to solve Shortest Path in a Grid with Obstacles Elimination
Augment the BFS state with the number of eliminations remaining; every edge still costs one step, so BFS on the augmented graph is still a shortest-path search.
Approach
- If
k >= m + n - 2, returnm + n - 2— a monotone path has at mostm + n - 3interior cells to clear. - BFS from
(0, 0, k)withvisited[r][c][rem]. - From
(r, c, rem)try the four neighbours; entering an obstacle needsrem > 0and leads torem - 1, an empty cell keepsrem. - Return the BFS layer at which
(m - 1, n - 1)is first dequeued (with anyrem), or-1.
Why it works
Each state captures everything that affects the future: the position and how many obstacles can still be removed. Every move costs exactly one step, so BFS over these states reaches each one at its minimum distance, and the first time the target cell appears is the shortest path that respects the elimination budget.
Complexity
- Time —
O(m · n · k) - Space —
O(m · n · k)
Pitfalls
- Marking a cell visited regardless of
remis wrong: reaching it later with more eliminations left can still lead to a shorter overall path. - Without the Manhattan shortcut, a large
kmakes the state spacem · n · k, which is needlessly large. - A 1 × 1 grid needs 0 steps.
Reference solution
Python
from typing import List
from collections import deque
def shortestPath(grid: List[List[int]], k: int) -> int:
m, n = len(grid), len(grid[0])
if k >= m + n - 2:
return m + n - 2
seen = [[[False] * (k + 1) for _ in range(n)] for _ in range(m)]
seen[0][0][k] = True
q = deque([(0, 0, k)])
steps = 0
while q:
for _ in range(len(q)):
r, c, rem = q.popleft()
if r == m - 1 and c == n - 1:
return steps
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
a, b = r + dr, c + dc
if 0 <= a < m and 0 <= b < n:
left = rem - grid[a][b]
if left >= 0 and not seen[a][b][left]:
seen[a][b][left] = True
q.append((a, b, left))
steps += 1
return -1JavaScript
var shortestPath = function(grid, k) {
var m = grid.length, n = grid[0].length;
if (k >= m + n - 2) return m + n - 2;
var K = k + 1;
var seen = new Array(m * n * K).fill(false);
var cur = [[0, 0, k]];
seen[k] = true;
var dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
for (var steps = 0; cur.length > 0; steps++) {
var next = [];
for (var i = 0; i < cur.length; i++) {
var r = cur[i][0], c = cur[i][1], rem = cur[i][2];
if (r === m - 1 && c === n - 1) return steps;
for (var d = 0; d < 4; d++) {
var a = r + dirs[d][0], b = c + dirs[d][1];
if (a < 0 || b < 0 || a >= m || b >= n) continue;
var left = rem - grid[a][b];
if (left < 0) continue;
var key = (a * n + b) * K + left;
if (!seen[key]) { seen[key] = true; next.push([a, b, left]); }
}
}
cur = next;
}
return -1;
};Also on the editorial tab: C, C#, C++, Go, Java, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 988 arrays problems · the whole catalogue
Learn the technique: Arrays · Matrix and Grid Traversal