Maximize cherries collected on a round trip by converting the problem into two simultaneous forward paths and solving with dynamic programming.
Problem Restatement
We are given an n x n grid.
Each cell contains one of three values:
| Value | Meaning |
|---|---|
1 | A cell with one cherry |
0 | An empty cell |
-1 | A thorn cell that cannot be passed |
We start at the top-left cell:
(0, 0)We need to travel to the bottom-right cell:
(n - 1, n - 1)using only:
right
downThen we return from the bottom-right cell to the top-left cell using only:
left
upWhenever we pass through a cell with a cherry, we collect it. After a cherry is collected, that cell becomes empty. A cherry can be collected at most once.
Return the maximum number of cherries we can collect. If there is no valid round trip, return 0.
The official constraints include 1 <= n <= 50, grid[i][j] is -1, 0, or 1, and the start and end cells are not thorns.
Input and Output
| Item | Meaning |
|---|---|
| Input | An n x n integer grid |
| Output | Maximum number of cherries collectible |
| Valid forward moves | Right or down |
| Valid return moves | Left or up |
| Blocked cell | -1 |
| Cherry rule | A cherry can be collected once |
Example function shape:
def cherryPickup(grid: list[list[int]]) -> int:
...Examples
Example 1
grid = [
[0, 1, -1],
[1, 0, -1],
[1, 1, 1],
]One optimal path goes down, down, right, right to reach the end.
Then the return path collects one remaining cherry.
The maximum total is:
5So the answer is:
5Example 2
grid = [
[1, 1, -1],
[1, -1, 1],
[-1, 1, 1],
]There is no valid way to complete the trip from start to end and back.
The answer is:
0First Thought: Pick One Path, Then Pick Return Path
A tempting approach is:
- Find a path from start to end that collects many cherries.
- Remove those cherries.
- Find the best return path.
This does not work.
The best first path may block the best total round trip. Sometimes we should collect fewer cherries on the way down so the return path can collect more.
So the two paths must be optimized together, not separately.
Key Insight
The return path from bottom-right to top-left using left and up can be reversed.
After reversing, it becomes another path from top-left to bottom-right using right and down.
So the whole round trip can be seen as:
two people start at (0, 0)
both move to (n - 1, n - 1)
each step is either right or downBoth people move at the same time.
If both people land on the same cell, we count its cherry once.
This converts the problem into a dynamic programming problem over two simultaneous paths. This is the standard formulation for this problem.
Algorithm
Let:
dp[k][r1][r2]mean the maximum cherries collected after both people have taken exactly k steps.
At step k:
person 1 is at (r1, c1)
person 2 is at (r2, c2)Since each person has taken k total moves:
c1 = k - r1
c2 = k - r2So we only need k, r1, and r2.
For each state:
- Skip it if either position is outside the grid.
- Skip it if either position is a thorn.
- Try the four possible previous states:
- both came from above
- person 1 from above, person 2 from left
- person 1 from left, person 2 from above
- both came from left
- Add cherries from the current cells.
- If both people are on the same cell, add the cherry once.
The answer is:
max(0, dp[2 * n - 2][n - 1][n - 1])We use max(0, ...) because unreachable states are stored as negative infinity.
Correctness
Reversing the return trip transforms it into a second forward trip from (0, 0) to (n - 1, n - 1). Therefore every valid round trip corresponds to two simultaneous forward paths, and every pair of forward paths corresponds to one round trip.
At step k, both paths have made the same number of moves. If one path is at row r, its column is forced to be k - r. Therefore the state dp[k][r1][r2] fully describes both positions.
The transition checks all possible previous moves for both paths. Since each person can only move right or down, their previous cell must have been either above or left. The four previous-state combinations cover all valid ways to arrive at the current pair of cells.
The value added at each state is exactly the number of new cherries collected at the two current positions. If the two people stand on different cells, both cherries may be counted. If they stand on the same cell, the cherry is counted once, matching the rule that each cherry can be collected only once.
The DP processes steps in increasing order, so every previous state is already computed before it is used.
Thus the final state gives the maximum cherries over all valid pairs of paths. If the final state is unreachable, the correct answer is 0.
Complexity
Let n be the grid size.
| Metric | Value | Why |
|---|---|---|
| Time | O(n^3) | There are O(n) steps and O(n^2) row pairs per step |
| Space | O(n^3) | The full DP table stores every step and row pair |
This can be optimized to O(n^2) space by keeping only the previous step, but the full DP version is easier to read.
Implementation
class Solution:
def cherryPickup(self, grid: list[list[int]]) -> int:
n = len(grid)
neg_inf = float("-inf")
steps = 2 * n - 1
dp = [
[[neg_inf] * n for _ in range(n)]
for _ in range(steps)
]
dp[0][0][0] = grid[0][0]
for k in range(1, steps):
for r1 in range(n):
for r2 in range(n):
c1 = k - r1
c2 = k - r2
if c1 < 0 or c1 >= n:
continue
if c2 < 0 or c2 >= n:
continue
if grid[r1][c1] == -1:
continue
if grid[r2][c2] == -1:
continue
best_prev = neg_inf
for prev_r1 in (r1, r1 - 1):
for prev_r2 in (r2, r2 - 1):
if prev_r1 < 0 or prev_r2 < 0:
continue
best_prev = max(
best_prev,
dp[k - 1][prev_r1][prev_r2],
)
if best_prev == neg_inf:
continue
cherries = grid[r1][c1]
if r1 != r2 or c1 != c2:
cherries += grid[r2][c2]
dp[k][r1][r2] = best_prev + cherries
return max(0, dp[steps - 1][n - 1][n - 1])Code Explanation
We use neg_inf for impossible states:
neg_inf = float("-inf")The number of simultaneous steps from (0, 0) to (n - 1, n - 1) is:
steps = 2 * n - 1That includes step 0.
The start state is:
dp[0][0][0] = grid[0][0]At each step, columns are derived from rows:
c1 = k - r1
c2 = k - r2If either column is outside the grid, the state is impossible.
If either cell is a thorn, the state is impossible:
if grid[r1][c1] == -1:
continue
if grid[r2][c2] == -1:
continueFor each current row, the previous row was either the same row or one row above:
for prev_r1 in (r1, r1 - 1):
for prev_r2 in (r2, r2 - 1):Same row means the person came from the left.
One row above means the person came from above.
Then we count cherries:
cherries = grid[r1][c1]If the two people are on different cells, add both cells:
if r1 != r2 or c1 != c2:
cherries += grid[r2][c2]Finally, return the best valid result, or 0 if no valid path exists.
Testing
def run_tests():
s = Solution()
assert s.cherryPickup([
[0, 1, -1],
[1, 0, -1],
[1, 1, 1],
]) == 5
assert s.cherryPickup([
[1, 1, -1],
[1, -1, 1],
[-1, 1, 1],
]) == 0
assert s.cherryPickup([[0]]) == 0
assert s.cherryPickup([[1]]) == 1
assert s.cherryPickup([
[1, 1],
[1, 1],
]) == 4
assert s.cherryPickup([
[0, 1, 1],
[1, -1, 1],
[1, 1, 1],
]) == 7
print("all tests passed")
run_tests()| Test | Why |
|---|---|
| Standard example | Checks round-trip maximum |
| Blocked grid | No valid complete route |
[[0]] | Single empty start/end cell |
[[1]] | Single cherry counted once |
Full 2 x 2 grid | All cherries can be collected |
| Grid with center thorn | Paths must route around obstacle |