AlgoMaster Logo

Minimum Obstacle Removal to Reach Corner

hardFrequencyUpdated September 21, 2026

Understanding the Problem

We have a grid where each cell is either empty (0) or an obstacle (1). We need to travel from the top-left corner to the bottom-right corner, moving in 4 directions. We can remove obstacles along the way, but we want to remove as few as possible.

This is a shortest path problem in a different form. Moving to an empty cell costs 0 (no removal needed), and moving to an obstacle costs 1 (we remove it). We are looking for the path from (0, 0) to (m-1, n-1) with the minimum total cost, where every edge costs either 0 or 1.

Two algorithms apply. Dijkstra's algorithm handles any non-negative edge weights and gives the answer directly. When weights are limited to 0 and 1, a deque-based variant called 0-1 BFS reaches the same answer in O(V + E) time, dropping the logarithmic factor that the priority queue adds.

Key Constraints:

  • 2 <= m * n <= 10^5: the grid holds up to 100,000 cells, so an O(m n log(m n)) solution is fast enough and an O(m n) solution is comfortable.
  • grid[i][j] is either 0 or 1: edge weights are limited to 0 and 1, which is the condition that makes 0-1 BFS applicable.
  • grid[0][0] == 0 and grid[m - 1][n - 1] == 0: the start and end cells are empty, so we never pay to remove an obstacle at the corners.

Approach 1: Dijkstra's Algorithm (Min-Heap)

Intuition

Plain BFS finds the shortest path in an unweighted graph, where shortest means fewest edges. This graph is weighted: moving to an empty cell costs 0, and moving to an obstacle costs 1. Running plain BFS would minimize the number of cells visited, not the number of obstacles removed.

Dijkstra's algorithm handles this directly. Treat each cell as a node and each move to an adjacent cell as an edge whose weight equals the destination cell's value (0 or 1). Dijkstra's finds the path that minimizes total weight, which is the minimum number of obstacles removed.

The cost is the priority queue, which adds a log factor to every cell processed. With up to 10^5 cells, that is fast enough.

Algorithm

  1. Create a 2D distance array dist of size m x n, initialized to infinity. Set dist[0][0] = 0.
  2. Push (0, 0, 0) into a min-heap (priority queue), where the tuple is (cost, row, col).
  3. While the heap is not empty, pop the element with the smallest cost.
  4. If we have reached (m-1, n-1), return the cost.
  5. If the popped cost is greater than dist[row][col], skip it (we already found a better path).
  6. For each of the 4 neighbors, calculate the new cost as cost + grid[neighborRow][neighborCol].
  7. If the new cost is less than dist[neighborRow][neighborCol], update dist and push the neighbor into the heap.
  8. Return dist[m-1][n-1].

Example Walkthrough

1Start: dist[0][0]=0, push (cost=0, 0, 0) to min-heap
0
1
2
0
cost=0
0
1
1
1
1
1
0
2
1
1
0
1/7

Code

The priority queue is the only part of Dijkstra's that exceeds linear time. Because edge weights are limited to 0 and 1, the next approach maintains the same processing order with a deque and removes the heap entirely.

Approach 2: 0-1 BFS (Optimal)

Intuition

When edge weights are limited to 0 and 1, a priority queue is not needed to process nodes in order of increasing distance. A deque (double-ended queue) maintains that order in O(1) per operation.

In plain BFS, every edge has weight 1, so the queue keeps nodes ordered by distance from the source. Here there are two kinds of edges. A 0-weight edge leads to a neighbor at the same distance as the current cell. A 1-weight edge leads to a neighbor one unit farther.

To preserve the ordering, push 0-cost neighbors to the front of the deque (same distance, process them next) and 1-cost neighbors to the back (one unit farther, process them later). The deque stays ordered by distance, the same property Dijkstra's priority queue provides, without the logarithmic cost.

Algorithm

  1. Create a 2D distance array dist of size m x n, initialized to infinity. Set dist[0][0] = 0.
  2. Create a deque and push (0, 0) to it.
  3. While the deque is not empty, pop from the front.
  4. For each of the 4 neighbors, calculate the new cost as dist[row][col] + grid[neighborRow][neighborCol].
  5. If the new cost is less than dist[neighborRow][neighborCol], update it. If the edge weight is 0 (neighbor is empty), push to the front of the deque. If the edge weight is 1 (neighbor is an obstacle), push to the back.
  6. Return dist[m-1][n-1].

Example Walkthrough

1Start: dist[0][0]=0, deque=[(0,0)]
0
1
2
0
cost=0
0
1
1
1
1
1
0
2
1
1
0
1/7

Code