Find Paths Across a Weighted Binary Grid
GoogleSoftware Engineer · OnsiteLast updated
Implement algorithms to analyze top-to-bottom paths in a blocked, cell-weighted grid: reachability check, deterministic path enumeration, and minimum-cost path finding with lexicographic tie-breaking.
Problem Statement
You are given an R x C grid of integers where:
-1represents a blocked cell (obstacle).- Any integer
w >= 0represents an open cell with an entry cost ofw.
A valid path across the grid must:
- Start at any open cell in the top row (
row = 0). - End at any open cell in the bottom row (
row = R - 1). - Move between 4-directionally adjacent open cells (up, down, left, right).
- Be a simple path (no cell is visited more than once).
The total cost of a path is the sum of costs of all cells on the path, including the starting cell.
Implement the class GridPathSolver with three methods:
class GridPathSolver:
def is_reachable(self, grid: list[list[int]]) -> bool:
"""Returns True if there is at least one valid path from row 0 to row R - 1, False otherwise."""
pass
def enumerate_paths(self, grid: list[list[int]], max_paths: int) -> list[list[tuple[int, int]]]:
"""Returns up to `max_paths` valid paths, ordered lexicographically by cell coordinates.
Lexicographical order compares coordinate tuples (r, c) sequentially."""
pass
def min_cost_path(self, grid: list[list[int]]) -> tuple[int, list[tuple[int, int]]]:
"""Returns a tuple (cost, path) of the minimum-cost valid path.
If multiple paths share the minimum cost, return the lexicographically smallest path.
If no valid path exists, return (-1, [])."""
pass
Constraints
1 <= R, C <= 50-1 <= grid[r][c] <= 10001 <= max_paths <= 100R * C <= 500forenumerate_pathsandmin_cost_path
Examples
Example 1
grid = [
[2, 3, -1],
[-1, 1, 4],
[ 5, 2, 1]
]
solver = GridPathSolver()
# 1. Reachability
solver.is_reachable(grid) # Returns: True
# 2. Minimum-cost path
solver.min_cost_path(grid)
# Returns: (7, [(0, 1), (1, 1), (2, 1)])
# Cost = 3 + 1 + 2 = 6? Wait: (0, 1)=3, (1, 1)=1, (2, 1)=2 => total = 6.
# Another option: (0, 1)=3, (1, 1)=1, (1, 2)=4, (2, 2)=1 => 9.
# Min cost is 6 with path [(0, 1), (1, 1), (2, 1)].
Example 2 (Single-Row Grid)
grid = [[-1, 4, 2]]
solver = GridPathSolver()
solver.is_reachable(grid) # Returns: True
solver.min_cost_path(grid)
# Returns: (2, [(0, 2)])
# Starting and ending on row 0 (which is also row R-1).
Example 3 (Unreachable Grid)
grid = [
[ 1, -1],
[-1, 1]
]
solver = GridPathSolver()
solver.is_reachable(grid) # Returns: False
solver.min_cost_path(grid) # Returns: (-1, [])
Hints
Hint 1
For reachability, a multi-source BFS or DFS seeded with all open cells in the top row suffices.
Hint 2
For deterministic enumeration, use backtracking while visiting neighbors in ascending coordinate order (e.g., up, left, right, down) and terminate early once `max_paths` is reached.
Hint 3
For minimum-cost path, use Dijkstra's algorithm initialized with all open cells in row 0; to break ties lexicographically, include the path or path predecessor comparisons when distances are equal.
Follow-up questions
- How would you handle negative edge weights or negative entry costs if the grid allowed them?
- If the grid is very wide (R is small, C is large, e.g., 5 x 100,000), how can you optimize the Dijkstra search or space complexity?
- How can you avoid storing full paths in priority queue nodes when performing lexicographic tie-breaking in Dijkstra's algorithm?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.