Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 20 – Graph patterns: traversal

BFS with extra state: keys and wall breaks

Find shortest paths when you may break up to k walls or must collect keys: put the extra state in every BFS node, count the states first, then prune.

  • Advanced
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 9 of 35 minutes free
Free: The problem: a robot that may drill through rock · The brute force: try every set of drilled cells · What to notice. With a pass: the other 9 sections and 3 interview answers, about 26 minutes.

What you will learn

  • Add the remaining wall breaks or the keys held to the state of a BFS
  • Estimate the number of states before writing the search
  • Track visited states instead of visited cells
  • Remove dominated states with one best value per cell
  • Encode collected keys as a bitmask and test doors with bit operations

Before you start

What you’ll learn · Before you start

What you will learn

  • Add the remaining wall breaks or the keys held to the state of a BFS
  • Estimate the number of states before writing the search
  • Track visited states instead of visited cells
  • Remove dominated states with one best value per cell
  • Encode collected keys as a bitmask and test doors with bit operations

Before you start

On this page

The shortest path in a grid when you may break through up to k obstacles is a BFS whose nodes carry more than the cell. Two visits to the same cell differ in how many breaks are still allowed, so the node becomes (row, column, breaks left), and the first time the search reaches the target in any such state is the fewest steps. The same idea covers doors that need keys, fuel that runs out and anything else that changes which moves are allowed. The work is in choosing that extra state and counting the states before you code.

The problem: a robot that may drill through rock

The problem

A collapsed mine is a grid of rows strings: '.' is open and '#' is rock. A rescue robot starts at the top-left cell and must reach the bottom-right cell, moving one cell up, down, left or right per step. It may drill through at most k rock cells; entering a rock cell takes one step like any other. Return the fewest steps, or −1. Limits: 1 ≤ rows, cols ≤ 40 and 0 ≤ k ≤ rows × cols; the two corner cells are open.

  1. On the 6 × 7 mine in the diagram, the robot needs 17 steps with k = 0, 13 with k = 1 and 11 with k = 2.
  2. ["..##", "##..", "...."] with k = 0 gives −1: the start is walled in. With k = 1 it gives 5.
  3. A 40 × 40 block of rock, open only at the two corners, gives 78 steps once k is at least 77.
The 6 by 7 mine with the 13-step route for one drill, numbered 0 to 13, drilling through the rock at row 2, column 4.0123456012345015672348910111213the route, step by steprockrock drilled through: the one drill of k = 1

The mine, and the fewest steps when one rock cell may be drilled

Text description of the diagram

The mine has 6 rows, numbered 0 to 5 from the top, and 7 columns, numbered 0 to 6 from the left. Rock cells are grey; open cells are white. The robot starts at row 0, column 0 and must reach row 5, column 6.

The route for k = 1 is shaded and each of its cells carries its step number:

  1. Steps 0 to 2 go down column 0 to row 2, steps 3 and 4 go right along row 2 to column 2.
  2. Steps 5 to 7 go up to row 1 and right along it to column 4.
  3. Step 8 drills through the rock at row 2, column 4, outlined with a dashed line.
  4. Steps 9 to 12 go to row 2, column 5 and down column 5 to row 5; step 13 is the exit at row 5, column 6.

Without a drill the shortest route takes 17 steps, round the top right; with two drills it takes 11, the fewest any route can take on this map.

The brute force: try every set of drilled cells

Choose which rock cells to drill, open them, and run a plain BFS; try every set of at most k cells and keep the best answer. It is correct, and on the mine it is quick, because the mine has only 16 rock cells:

Every set of drilled rock cells, then a plain BFS Python · brute/drill_brute.py
"""Fewest steps when up to k rock cells may be drilled, by brute force: try every set of at most k rock cells, drill
them, and run a plain BFS. Correct, and the number of sets explodes with k."""
from collections import deque
from itertools import combinations
from math import comb

MINE = [".#.#...",
        ".#...#.",
        "...##..",
        "...##.#",
        ".###..#",
        ".##.#.."]


def plain_bfs(grid):
    """Fewest steps from the top-left to the bottom-right cell over open cells, or -1."""
    rows, cols = len(grid), len(grid[0])
    dist = [[-1] * cols for _ in range(rows)]
    dist[0][0] = 0
    queue = deque([(0, 0)])
    while queue:
        r, c = queue.popleft()
        for nr, nc in ((r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)):
            if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == "." and dist[nr][nc] == -1:
                dist[nr][nc] = dist[r][c] + 1
                queue.append((nr, nc))
    return dist[rows - 1][cols - 1]


def by_brute_force(grid, k):
    rocks = [(r, c) for r, row in enumerate(grid) for c, x in enumerate(row) if x == "#"]
    best, searches = -1, 0
    for size in range(k + 1):
        for drilled in combinations(rocks, size):
            opened = [list(row) for row in grid]
            for r, c in drilled:
                opened[r][c] = "."
            searches += 1
            d = plain_bfs(["".join(row) for row in opened])
            if d != -1 and (best == -1 or d < best):
                best = d
    return best, searches


rocks = sum(row.count("#") for row in MINE)
print(f"The mine: {len(MINE)} x {len(MINE[0])} cells, {rocks} of them rock")
for k in range(4):
    best, searches = by_brute_force(MINE, k)
    print(f"  k = {k}: {best} steps, after {searches:,} {'search' if searches == 1 else 'searches'}")
print(f"A 40 x 40 mine with 480 rock cells and k = 5: {sum(comb(480, s) for s in range(6)):,} searches")

Output

The mine: 6 x 7 cells, 16 of them rock
  k = 0: 17 steps, after 1 search
  k = 1: 13 steps, after 17 searches
  k = 2: 11 steps, after 137 searches
  k = 3: 11 steps, after 697 searches
A 40 x 40 mine with 480 rock cells and k = 5: 210,147,849,977 searches

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 drill_brute.py

The searches grow like the number of sets: 1, 17, 137 and 697 for k up to 3. A 40 × 40 mine with 480 rock cells and k = 5 needs 210,147,849,977 of them, each a search of up to 1,600 cells, and k can be in the hundreds.

What to notice

The cell alone does not decide what the robot can do next: the drills it has left matter too. A robot at a cell with two drills left can follow routes that the same robot with none cannot. Make the drills left part of the node, and the problem becomes a plain BFS over at most rows × cols × (k + 1) states, each reached at most once. Keys work alike: a robot that holds the key to a door has moves that one without it lacks, so the set of keys held joins the state.

The state: row, column and drills left Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • How many states, before any code
    • Why the visited check is per state: the bug reproduced
    • The code in five languages
  • Keeping only the best state per cell, measured
  • Keys and doors: a bitmask in the state
  • Variants
  • Edge cases and common bugs
  • Exercise: collect every key
    • Fewest steps to collect every key
  • A practice set with answers
  • Check yourself
  • Interview questions
    • In a grid BFS that may break through up to k walls, why is k never usefully larger than rows + cols − 3, and what does that cap save?
    • A teammate's solution to the shortest path with up to k wall breaks marks cells as visited. It passes the samples and fails hidden tests. Give a small failing input, explain why it fails, fix it, and say what the fix costs in memory on a 40 × 40 grid with k = 40.
    • A warehouse robot on a 50 × 50 floor must collect up to 12 keys, and doors open only with their keys. Estimate the states of a BFS over (cell, keys held), decide whether it fits in time and memory, and say what you would change if there were 20 keys.
    • Breaking a wall now costs 5 units of time while an open step costs 1, and breaks are unlimited. Why does BFS no longer give the fastest route, what replaces it, and what changes when breaks are also limited to k?
  • Key takeaways

5 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 21 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.