1219. Path with Maximum Gold - Explanation
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
- Iterate through every cell in the grid. For each cell that contains gold, start a
dfsfrom that cell. - In the
dfs, mark the current cell as visited by adding it to a set. - Explore all four directions (up, down, left, right). For each valid neighbor that contains gold and has not been visited, recursively call
dfs. - Track the maximum gold collected among all paths from the current cell.
- Before returning, remove the current cell from the visited set (backtrack) so it can be used in other paths.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
Where 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
- Iterate through every cell in the grid. For each cell that contains gold, start a
dfsfrom that cell. - In the
dfs, save the current cell's gold value and set the cell to0(marking it as visited). - Explore all four directions. For each valid neighbor, recursively call
dfsand track the maximum gold from neighbors. - Restore the cell's original gold value before returning (backtrack).
- Return the current cell's gold plus the maximum gold from any path extending from it.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity: for recursion stack.
Where 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
- Assign a unique index to each cell containing gold.
- 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.
- For each state in the queue, update the maximum gold collected.
- 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. - 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
Where 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.