Before attempting this problem, you should be comfortable with:
Two robots start at the top corners of the grid and move down simultaneously, one row at a time. At each row, each robot can move diagonally left, straight down, or diagonally right. We track both robot positions and explore all 9 combinations of moves (3 choices for robot 1 times 3 choices for robot 2). If both robots land on the same cell, we only count the cherries once. We enforce c1 <= c2 to avoid duplicate states since the robots are interchangeable.
dfs(r, c1, c2) where r is the current row, c1 is robot 1's column, and c2 is robot 2's column.c1 or c2 is out of bounds, or c1 > c2, return 0.r equals ROWS - 1, return the sum of cherries at both positions (counting once if they overlap).c1_d, c2_d in [-1, 0, 1]):dfs(r + 1, c1 + c1_d, c2 + c2_d).class Solution:
def cherryPickup(self, grid: List[List[int]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
def dfs(r, c1, c2):
if c1 < 0 or c2 < 0 or c1 >= COLS or c2 >= COLS or c1 > c2:
return 0
if r == ROWS - 1:
return grid[r][c1] + (grid[r][c2] if c1 != c2 else 0)
res = 0
for c1_d in [-1, 0, 1]:
for c2_d in [-1, 0, 1]:
res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d))
return res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0)
return dfs(0, 0, COLS - 1)Where is the number of rows and is the number of columns in the grid.
The recursive solution recomputes the same states multiple times. Since the state is defined by (r, c1, c2), we can use a 3D cache to store results for previously visited states. When we encounter a state we have already solved, we return the cached value instead of recomputing it.
[ROWS][COLS][COLS] initialized to -1.class Solution:
def cherryPickup(self, grid: List[List[int]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
cache = {}
def dfs(r, c1, c2):
if (r, c1, c2) in cache:
return cache[(r, c1, c2)]
if c1 == c2 or min(c1, c2) < 0 or max(c1, c2) >= COLS:
return 0
if r == ROWS - 1:
return grid[r][c1] + grid[r][c2]
res = 0
for c1_d in [-1, 0, 1]:
for c2_d in [-1, 0, 1]:
res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d))
cache[(r, c1, c2)] = res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0)
return cache[(r, c1, c2)]
return dfs(0, 0, COLS - 1)Where is the number of rows and is the number of columns in the grid.
Instead of using recursion with memoization, we can build the solution iteratively from the bottom row up. For each cell combination at row r, we look at all possible transitions from row r+1 and take the maximum. This eliminates recursion overhead and makes the computation order explicit.
[ROWS][COLS][COLS].r = ROWS-1 to 0).(c1, c2) pair at row r:(nc1, nc2) positions in row r+1.dp[r][c1][c2].dp[0][0][COLS-1].class Solution:
def cherryPickup(self, grid: List[List[int]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
dp = [[[0] * COLS for _ in range(COLS)] for _ in range(ROWS)]
for r in range(ROWS - 1, -1, -1):
for c1 in range(COLS):
for c2 in range(COLS):
res = grid[r][c1]
if c1 != c2:
res += grid[r][c2]
if r != ROWS - 1:
max_cherries = 0
for d1 in [-1, 0, 1]:
for d2 in [-1, 0, 1]:
nc1, nc2 = c1 + d1, c2 + d2
if 0 <= nc1 < COLS and 0 <= nc2 < COLS:
max_cherries = max(max_cherries, dp[r + 1][nc1][nc2])
res += max_cherries
dp[r][c1][c2] = res
return dp[0][0][COLS - 1]Where is the number of rows and is the number of columns in the grid.
Since each row's computation only depends on the row below it, we do not need to store the entire 3D array. We can use two 2D arrays: one for the current row and one for the previous row (the row below). After processing each row, we swap them. This reduces space complexity from O(n * m^2) to O(m^2).
dp array of size [COLS][COLS] for the previous row's results.cur_dp array for the current row.(c1, c2) pair where c1 <= c2:dp array (previous row).dp = cur_dp.dp[0][COLS-1].class Solution:
def cherryPickup(self, grid: List[List[int]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
dp = [[0] * COLS for _ in range(COLS)]
for r in reversed(range(ROWS)):
cur_dp = [[0] * COLS for _ in range(COLS)]
for c1 in range(COLS):
for c2 in range(c1, COLS):
max_cherries = 0
cherries = grid[r][c1] + (grid[r][c2] if c1 != c2 else 0)
for d1 in [-1, 0, 1]:
for d2 in [-1, 0, 1]:
nc1, nc2 = c1 + d1, c2 + d2
if 0 <= nc1 < COLS and 0 <= nc2 < COLS:
max_cherries = max(max_cherries, cherries + dp[nc1][nc2])
cur_dp[c1][c2] = max_cherries
dp = cur_dp
return dp[0][COLS - 1]Where is the number of rows and is the number of columns in the grid.
Forgetting to check if both robots land on the same cell and counting cherries twice. When c1 == c2, you must only add grid[r][c1] once, not grid[r][c1] + grid[r][c2].
# Wrong: always adds both
res = grid[r][c1] + grid[r][c2]
# Correct: check for overlap
res = grid[r][c1] + (grid[r][c2] if c1 != c2 else 0)Treating this like a general grid traversal where robots can move in any direction. Each robot can only move down-left, down, or down-right (3 choices), not up or stay in place. This gives 9 total combinations per step, not 16.
Using only (r, c1) or (r, c2) for caching, forgetting that the state depends on both robot positions simultaneously. The correct state is (r, c1, c2) since both robots move together row by row.
Checking r == ROWS instead of r == ROWS - 1 for the base case. Since indexing is 0-based, the last row is at index ROWS - 1, and that is where you should collect final cherries and stop recursion.