1219. Path with Maximum Gold - Explanation

Problem Link



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Backtracking - Exploring all possible paths and undoing choices to try different options
  • Depth First Search (DFS) - Traversing a grid by exploring as far as possible along each branch
  • 2D Grid Traversal - Moving in four directions (up, down, left, right) and handling boundaries
  • Bitmask (for BFS solution) - Using bits to efficiently track visited states

1. Backtracking (DFS) - I

Intuition

We want to collect the maximum amount of gold by traversing the grid. Since we can start from any cell containing gold and move in four directions without revisiting cells, this naturally fits a backtracking approach. We try starting from every gold cell and explore all possible paths, keeping track of the maximum gold collected. The key insight is that we need to "undo" our visit after exploring a path so other paths can use that cell.

Algorithm

  1. Iterate through every cell in the grid. For each cell that contains gold, start a dfs from that cell.
  2. In the dfs, mark the current cell as visited by adding it to a set.
  3. Explore all four directions (up, down, left, right). For each valid neighbor that contains gold and has not been visited, recursively call dfs.
  4. Track the maximum gold collected among all paths from the current cell.
  5. Before returning, remove the current cell from the visited set (backtrack) so it can be used in other paths.
  6. Return the maximum gold found across all starting positions.
class Solution:
    def getMaximumGold(self, grid: list[list[int]]) -> int:
        ROWS, COLS = len(grid), len(grid[0])
        directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]

        def dfs(r, c, visit):
            if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0 or (r, c) in visit:
                return 0

            visit.add((r, c))
            res = grid[r][c]

            for dr, dc in directions:
                res = max(res, grid[r][c] + dfs(r + dr, c + dc, visit))

            visit.remove((r, c))
            return res

        res = 0
        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] != 0:
                    res = max(res, dfs(r, c, set()))
        return res

Time & Space Complexity

  • Time complexity: O(N3N)O(N * 3 ^ N)
  • Space complexity: O(N)O(N)

Where NN is the number of cells which contain gold.


2. Backtracking (DFS) - II

Intuition

Instead of using a separate visited set, we can mark cells as visited by temporarily setting them to zero. Since zero represents an empty cell (no gold), the dfs will naturally skip it. After exploring all paths from a cell, we restore its original value. This approach saves space and can be slightly faster since we avoid set operations.

Algorithm

  1. Iterate through every cell in the grid. For each cell that contains gold, start a dfs from that cell.
  2. In the dfs, save the current cell's gold value and set the cell to 0 (marking it as visited).
  3. Explore all four directions. For each valid neighbor, recursively call dfs and track the maximum gold from neighbors.
  4. Restore the cell's original gold value before returning (backtrack).
  5. Return the current cell's gold plus the maximum gold from any path extending from it.
  6. Return the maximum gold found across all starting positions.
class Solution:
    def getMaximumGold(self, grid: list[list[int]]) -> int:
        ROWS, COLS = len(grid), len(grid[0])
        directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]

        def dfs(r, c):
            if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0:
                return 0

            gold = grid[r][c]
            grid[r][c] = 0
            res = 0

            for dr, dc in directions:
                res = max(res, dfs(r + dr, c + dc))

            grid[r][c] = gold
            return gold + res

        res = 0
        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] != 0:
                    res = max(res, dfs(r, c))
        return res

Time & Space Complexity

  • Time complexity: O(N3N)O(N * 3 ^ N)
  • Space complexity: O(N)O(N) for recursion stack.

Where NN is the number of cells which contain gold.


3. Backtracking (BFS)

Intuition

We can also solve this problem using BFS with bitmask state tracking. Each cell containing gold is assigned a unique index, and we use a bitmask to track which cells have been visited along the current path. This allows us to explore all possible paths level by level while ensuring we do not revisit any cell within the same path.

Algorithm

  1. Assign a unique index to each cell containing gold.
  2. For each gold cell, start a BFS with the initial state containing the cell's position, the gold collected so far, and a bitmask marking this cell as visited.
  3. For each state in the queue, update the maximum gold collected.
  4. Explore all four neighbors. If a neighbor contains gold and has not been visited in the current path (check the bitmask), add a new state to the queue with updated gold and an updated bitmask.
  5. Continue until the queue is empty and return the maximum gold found.
class Solution:
    def getMaximumGold(self, grid: list[list[int]]) -> int:
        ROWS, COLS = len(grid), len(grid[0])
        directions = [1, 0, -1, 0, 1]
        index = [[0] * COLS for _ in range(ROWS)]
        idx = 0

        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] != 0:
                    index[r][c] = idx
                    idx += 1

        res = 0
        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] > 0:
                    q = deque([(r, c, grid[r][c], 1 << index[r][c])])
                    while q:
                        row, col, gold, mask = q.popleft()
                        res = max(res, gold)

                        for i in range(4):
                            nr, nc = row + directions[i], col + directions[i + 1]
                            if 0 <= nr < ROWS and 0 <= nc < COLS and grid[nr][nc] > 0:
                                idx = index[nr][nc]
                                if not (mask & (1 << idx)):
                                    q.append((nr, nc, gold + grid[nr][nc], mask | (1 << idx)))

        return res

Time & Space Complexity

  • Time complexity: O(N3N)O(N * 3 ^ N)
  • Space complexity: O(N)O(N)

Where NN is the number of cells which contain gold.


Common Pitfalls

Forgetting to Backtrack

The most common mistake is forgetting to restore the cell's state after exploring a path. Whether using a visited set or modifying the grid directly, failing to backtrack means other paths starting from different cells cannot use that position. Always remove the cell from the visited set or restore its original value before returning.

Starting Only from Corner Cells

Some solutions incorrectly assume the optimal path must start from a corner or edge. The problem allows starting from any cell containing gold, so you must try all possible starting positions. Missing interior starting cells can lead to suboptimal results.

Not Handling Zero Cells Correctly

Cells with value zero cannot be visited, but some solutions forget to check for zeros when determining valid neighbors. Additionally, when using the grid modification approach to mark cells as visited (setting them to zero), ensure you save the original value before modification so it can be properly restored during backtracking.