Dynamic Programming · 10 of 60
Gold Mine Problem
Walk through a grid of gold from the left edge to the right edge, stepping up-right, right or down-right, and collect as much gold as you can.
- Simple way
- time O(m * 3^n)
room O(n) - Clever way
- time O(m * n)
room O(m * n)
The problem, simply
A gold mine is a grid with m rows and n columns. Each cell holds some gold. A miner starts in any cell of the first column. From cell (r, c) the miner can step to (r - 1, c + 1) (up-right), (r, c + 1) (right) or (r + 1, c + 1) (down-right), as long as it stays inside the grid. The walk ends at the last column.
Give back the most gold the miner can collect.
A story to remember it
Picture it
The 3 by 3 mine. The miner enters on the left, in any row, and walks right one column at a time, going up-right, right or down-right. The best walk is 2, then 6, then 4: 12 gold.
- answer
Examples
A small mine
- In
mine = [[1,3,3],[2,1,4],[0,6,4]]- Out
12
Start at 2 (row 1), step down-right to 6, then right to 4. 2 + 6 + 4 = 12.
A bigger mine
- In
mine = [[1,3,1,5],[2,2,4,1],[5,0,2,3],[0,6,1,2]]- Out
16
Start at 5 (row 2), go up to 2, right to 4, up to 5. 5 + 2 + 4 + 5 = 16.
Rich top rows
- In
mine = [[10,33,13,15],[22,21,4,1],[5,0,2,3],[0,6,14,2]]- Out
83
22 + 33 + 13 + 15 = 83: start on row 1, step up, then keep going right.
One row
- In
mine = [[3,1,2]]- Out
6
There is only one walk: take everything.
One column
- In
mine = [[1],[5],[2]]- Out
5
The walk is a single cell. Pick the richest one.
Way 1: the simple way
From a cell, the best total is its own gold plus the best of the three cells it can step to. Ask that with recursion, and try every starting row.
- Outside the grid, or past the last column: 0 gold.
- Otherwise:
mine[r][c] + max(best(r-1, c+1), best(r, c+1), best(r+1, c+1)). - The answer is the largest
best(r, 0)over all rowsr.
Calls when the miner starts at row 1 of the 3 by 3 mine. A circle r,c is a cell (row, column). Each cell asks its up-right, right and down-right neighbors. The cells in the last column get asked again and again (faded). Double rings are the last column, where the walk ends.
- seen
class Solution {
int best(int[][] mine, int r, int c) {
int m = mine.length, n = mine[0].length;
if (r < 0 || r >= m || c >= n) return 0; // off the map
int upRight = best(mine, r - 1, c + 1);
int right = best(mine, r, c + 1);
int downRight = best(mine, r + 1, c + 1);
return mine[r][c] + Math.max(right, Math.max(upRight, downRight));
}
public int maxGold(int[][] mine) {
int answer = 0;
for (int r = 0; r < mine.length; r++) {
answer = Math.max(answer, best(mine, r, 0)); // try every starting row
}
return answer;
}
}How long it takes: every cell asks three cells, for n columns, so one start costs up to 3^n, and there are m starts: O(m * 3^n). Extra room: the chain of calls is n long, O(n).
Way 2: the clever way
There are only m * n cells, and best(r, c) is the same every time. Fill a table dp from the last column to the first.
- Last column:
dp[r][n-1] = mine[r][n-1]. - For
cfromn - 2down to 0, for every rowr:dp[r][c] = mine[r][c] + maxof the up to three cellsdp[r-1][c+1],dp[r][c+1],dp[r+1][c+1]that exist. - The answer is the largest number in column 0.
Fill the best gold from right to left
01 / 14
Work from the right edge backwards. In the last column the walk ends right there, so the best from each cell is just its own gold.
- looking now
- checking
- done
- answer
- just added
class Solution {
public int maxGold(int[][] mine) {
int m = mine.length, n = mine[0].length;
int[][] dp = new int[m][n]; // dp[r][c]: most gold from (r, c) to the right edge
for (int r = 0; r < m; r++) dp[r][n - 1] = mine[r][n - 1];
for (int c = n - 2; c >= 0; c--) { // right to left
for (int r = 0; r < m; r++) {
int right = dp[r][c + 1];
int upRight = (r > 0) ? dp[r - 1][c + 1] : 0;
int downRight = (r < m - 1) ? dp[r + 1][c + 1] : 0;
dp[r][c] = mine[r][c] + Math.max(right, Math.max(upRight, downRight));
}
}
int answer = 0;
for (int r = 0; r < m; r++) answer = Math.max(answer, dp[r][0]); // any start row
return answer;
}
}How long it takes: three looks per cell, O(m * n). Extra room: the table, O(m * n). Each column only needs the column to its right, so two columns of size m are enough for O(m).
Watch out for
- Edges of the grid. Row 0 has no up-right step and the last row has no down-right step. Using 0 for them is safe because gold is never negative.
- Filling the wrong way. Left to right also works, but then
dpmeans "most gold to reach this cell", and the answer is the max of the last column. Do not mix the two meanings. - A one column mine. The loop never runs, and the answer is the biggest single cell.
What interviewers ask next
- Print the walk. Start at the best cell of column 0 and keep stepping to the neighbor with the biggest
dpvalue, as in the last frame above. - What if cells can be negative (traps)? Then "off the map" must count as impossible, not 0: use a very small number for missing neighbors.
- What if the miner walks top to bottom instead, or can also step left? Top to bottom is the same idea turned sideways (this is the "minimum falling path sum" problem). Stepping left makes loops, so you need a graph search like Dijkstra instead.
- Two miners walk at the same time and gold is only collected once? That is "Cherry Pickup": track both miners together,
dp[c][r1][r2].
Remember this
Grown-up words: the sticky notes are the DP table, filling from the end is bottom-up DP, the three steps are the transitions, the repeated cells in the tree are overlapping subproblems, and the walk is a path in a grid.