1463. Cherry Pickup II
Problem Description
You have a rows x cols
matrix grid
representing a field of cherries, where grid[i][j]
represents the number of cherries at cell (i, j)
.
Two robots will collect cherries from this field:
- Robot #1 starts at the top-left corner
(0, 0)
- Robot #2 starts at the top-right corner
(0, cols - 1)
Both robots move simultaneously from the top row to the bottom row following these rules:
Movement Rules:
- From any cell
(i, j)
, a robot can move to one of three cells in the next row:(i + 1, j - 1)
- diagonally left(i + 1, j)
- straight down(i + 1, j + 1)
- diagonally right
- Robots cannot move outside the grid boundaries
- Both robots must reach the bottom row
Cherry Collection Rules:
- When a robot passes through a cell, it collects all cherries from that cell
- After collection, the cell becomes empty (0 cherries)
- If both robots occupy the same cell simultaneously, only one robot collects the cherries from that cell
The goal is to find the maximum total number of cherries that both robots can collect together by choosing optimal paths from the top row to the bottom row.
For example, if Robot #1 collects cherries from certain cells and Robot #2 collects from different cells (except when they're at the same position), the total collection is the sum of all cherries collected by both robots throughout their journey.
Intuition
The key insight is that both robots move simultaneously row by row, so we can think of their movement as a synchronized process. At any given moment, both robots will be in the same row, just at different column positions.
Since both robots start at row 0 and must reach the bottom row, and they can only move down (to the next row) with each step, we can process this problem row by row. This naturally leads to a dynamic programming approach where we track the state of both robots together.
The state we need to track is: "What's the maximum cherries we can collect when robot 1 is at position j1
and robot 2 is at position j2
in row i
?" This gives us a 3-dimensional DP state: f[i][j1][j2]
.
Why do we need to track both robots' positions simultaneously? Because when they land on the same cell, only one can collect the cherries. If we tried to find optimal paths for each robot independently, we'd incorrectly count cherries twice when their paths overlap.
For each state f[i][j1][j2]
, we need to consider where the robots could have come from in the previous row. Since each robot can move to three possible positions (left diagonal, straight down, right diagonal), and we have two robots, there are 3 × 3 = 9
possible previous states to consider.
The transition is straightforward:
- If the robots are at different positions (
j1 ≠ j2
), they collectgrid[i][j1] + grid[i][j2]
cherries - If they're at the same position (
j1 = j2
), they only collectgrid[i][j1]
cherries once
By building up the solution row by row and considering all possible movements, we ensure we find the optimal path for both robots that maximizes the total cherry collection.
Learn more about Dynamic Programming patterns.
Solution Approach
We implement a 3D dynamic programming solution where f[i][j1][j2]
represents the maximum cherries collected when robot 1 is at column j1
and robot 2 is at column j2
in row i
.
Initialization:
- Create a 3D array
f
with dimensions[m][n][n]
initialized to-1
, where-1
indicates an unreachable or unvisited state - Set
f[0][0][n-1] = grid[0][0] + grid[0][n-1]
as the starting position (robot 1 at top-left, robot 2 at top-right)
State Transition:
For each row i
from 1 to m-1
, and for each possible position combination (j1, j2)
where both robots can be:
-
Calculate the cherries collected at current positions:
x = grid[i][j1] + (0 if j1 == j2 else grid[i][j2])
This handles the case where both robots are at the same cell (collect cherries only once).
-
Check all possible previous positions:
- Robot 1 could have come from columns:
y1 ∈ {j1-1, j1, j1+1}
- Robot 2 could have come from columns:
y2 ∈ {j2-1, j2, j2+1}
- Robot 1 could have come from columns:
-
For each valid previous state where
f[i-1][y1][y2] != -1
:f[i][j1][j2] = max(f[i][j1][j2], f[i-1][y1][y2] + x)
The state transition formula is:
f[i][j1][j2] = max(f[i-1][y1][y2] + cherries_at_current_positions)
where y1 ∈ {j1-1, j1, j1+1}
and y2 ∈ {j2-1, j2, j2+1}
are valid positions within grid bounds.
Final Answer: After filling the entire DP table, the answer is the maximum value among all possible ending positions in the last row:
max(f[m-1][j1][j2] for all valid j1, j2)
The time complexity is O(m × n² × 9) = O(m × n²)
since for each of the m × n²
states, we check at most 9 previous states. The space complexity is O(m × n²)
for the 3D DP array.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider a 3×3 grid:
grid = [[3, 1, 1], [2, 5, 1], [1, 5, 5]]
Initial Setup:
- Robot 1 starts at (0, 0) with 3 cherries
- Robot 2 starts at (0, 2) with 1 cherry
- Initialize
f[0][0][2] = 3 + 1 = 4
Row 0 → Row 1 Transition:
From f[0][0][2] = 4
, we explore where both robots can move:
- Robot 1 from column 0 can go to columns: 0 or 1
- Robot 2 from column 2 can go to columns: 1 or 2
Possible transitions:
-
f[1][0][1]
: Robot 1 at (1,0), Robot 2 at (1,1)- Cherries collected: 2 + 5 = 7
- Total: 4 + 7 = 11
-
f[1][0][2]
: Robot 1 at (1,0), Robot 2 at (1,2)- Cherries collected: 2 + 1 = 3
- Total: 4 + 3 = 7
-
f[1][1][1]
: Robot 1 at (1,1), Robot 2 at (1,1) - same cell!- Cherries collected: 5 (only once, not 5+5)
- Total: 4 + 5 = 9
-
f[1][1][2]
: Robot 1 at (1,1), Robot 2 at (1,2)- Cherries collected: 5 + 1 = 6
- Total: 4 + 6 = 10
After row 1: f[1][0][1] = 11
, f[1][0][2] = 7
, f[1][1][1] = 9
, f[1][1][2] = 10
Row 1 → Row 2 Transition:
From f[1][0][1] = 11
:
- Robot 1 from column 0 → columns 0 or 1
- Robot 2 from column 1 → columns 0, 1, or 2
Key transitions:
f[2][0][2]
: 11 + (1 + 5) = 17f[2][1][2]
: 11 + (5 + 5) = 21
From f[1][1][2] = 10
:
- Robot 1 from column 1 → columns 0, 1, or 2
- Robot 2 from column 2 → columns 1 or 2
Key transitions:
f[2][0][2]
: max(17, 10 + (1 + 5)) = 17f[2][1][2]
: max(21, 10 + (5 + 5)) = 21f[2][2][2]
: 10 + 5 = 15 (same cell, collect once)
Final Answer:
Maximum among all f[2][j1][j2]
values = 21
The optimal paths are:
- Robot 1: (0,0) → (1,0) → (2,1), collecting 3 + 2 + 5 = 10
- Robot 2: (0,2) → (1,1) → (2,2), collecting 1 + 5 + 5 = 11
- Total: 21 cherries
Solution Implementation
1class Solution:
2 def cherryPickup(self, grid: List[List[int]]) -> int:
3 # Get dimensions of the grid
4 rows, cols = len(grid), len(grid[0])
5
6 # Initialize 3D DP table: dp[row][robot1_col][robot2_col]
7 # dp[i][j1][j2] represents max cherries collected when:
8 # - Both robots are at row i
9 # - Robot 1 is at column j1, Robot 2 is at column j2
10 dp = [[[-1] * cols for _ in range(cols)] for _ in range(rows)]
11
12 # Base case: robots start at top row
13 # Robot 1 starts at column 0, Robot 2 starts at last column
14 dp[0][0][cols - 1] = grid[0][0] + grid[0][cols - 1]
15
16 # Process each row from top to bottom
17 for row in range(1, rows):
18 # Try all possible positions for both robots in current row
19 for col1 in range(cols):
20 for col2 in range(cols):
21 # Calculate cherries collected at current positions
22 # If robots are at same position, only count cherries once
23 current_cherries = grid[row][col1]
24 if col1 != col2:
25 current_cherries += grid[row][col2]
26
27 # Check all possible previous positions for both robots
28 # Each robot can move diagonally left, straight down, or diagonally right
29 for prev_col1 in range(col1 - 1, col1 + 2):
30 for prev_col2 in range(col2 - 1, col2 + 2):
31 # Validate previous positions are within bounds
32 if (0 <= prev_col1 < cols and
33 0 <= prev_col2 < cols and
34 dp[row - 1][prev_col1][prev_col2] != -1):
35 # Update maximum cherries for current state
36 dp[row][col1][col2] = max(
37 dp[row][col1][col2],
38 dp[row - 1][prev_col1][prev_col2] + current_cherries
39 )
40
41 # Find maximum cherries from all possible ending positions
42 from itertools import product
43 max_cherries = max(
44 dp[-1][col1][col2]
45 for col1, col2 in product(range(cols), range(cols))
46 if dp[-1][col1][col2] != -1
47 )
48
49 return max_cherries
50
1class Solution {
2 public int cherryPickup(int[][] grid) {
3 int rows = grid.length;
4 int cols = grid[0].length;
5
6 // dp[row][col1][col2] represents the maximum cherries collected
7 // when robot1 is at position (row, col1) and robot2 is at position (row, col2)
8 int[][][] dp = new int[rows][cols][cols];
9
10 // Initialize all dp values to -1 (unvisited state)
11 for (int[][] layer : dp) {
12 for (int[] row : layer) {
13 Arrays.fill(row, -1);
14 }
15 }
16
17 // Initial state: robot1 starts at top-left (0, 0), robot2 starts at top-right (0, cols-1)
18 dp[0][0][cols - 1] = grid[0][0] + grid[0][cols - 1];
19
20 // Process each row from top to bottom
21 for (int row = 1; row < rows; row++) {
22 // Try all possible positions for robot1 in current row
23 for (int col1 = 0; col1 < cols; col1++) {
24 // Try all possible positions for robot2 in current row
25 for (int col2 = 0; col2 < cols; col2++) {
26 // Calculate cherries collected at current positions
27 // If both robots are at same position, count cherries only once
28 int currentCherries = grid[row][col1] + (col1 == col2 ? 0 : grid[row][col2]);
29
30 // Check all possible previous positions for robot1 (can move diagonally: left, down, right)
31 for (int prevCol1 = col1 - 1; prevCol1 <= col1 + 1; prevCol1++) {
32 // Check all possible previous positions for robot2
33 for (int prevCol2 = col2 - 1; prevCol2 <= col2 + 1; prevCol2++) {
34 // Validate previous positions are within bounds and reachable
35 if (prevCol1 >= 0 && prevCol1 < cols &&
36 prevCol2 >= 0 && prevCol2 < cols &&
37 dp[row - 1][prevCol1][prevCol2] != -1) {
38
39 // Update maximum cherries for current state
40 dp[row][col1][col2] = Math.max(
41 dp[row][col1][col2],
42 dp[row - 1][prevCol1][prevCol2] + currentCherries
43 );
44 }
45 }
46 }
47 }
48 }
49 }
50
51 // Find the maximum cherries collected at the bottom row
52 int maxCherries = 0;
53 for (int col1 = 0; col1 < cols; col1++) {
54 for (int col2 = 0; col2 < cols; col2++) {
55 maxCherries = Math.max(maxCherries, dp[rows - 1][col1][col2]);
56 }
57 }
58
59 return maxCherries;
60 }
61}
62
1class Solution {
2public:
3 int cherryPickup(vector<vector<int>>& grid) {
4 int rows = grid.size();
5 int cols = grid[0].size();
6
7 // dp[i][j1][j2] represents the maximum cherries collected when:
8 // - Both robots are at row i
9 // - Robot 1 is at column j1
10 // - Robot 2 is at column j2
11 int dp[rows][cols][cols];
12 memset(dp, -1, sizeof(dp));
13
14 // Initial state: robots start at top-left and top-right corners
15 dp[0][0][cols - 1] = grid[0][0] + grid[0][cols - 1];
16
17 // Process each row from top to bottom
18 for (int row = 1; row < rows; ++row) {
19 // Try all possible positions for robot 1 in current row
20 for (int col1 = 0; col1 < cols; ++col1) {
21 // Try all possible positions for robot 2 in current row
22 for (int col2 = 0; col2 < cols; ++col2) {
23 // Calculate cherries collected at current positions
24 // If both robots are at same position, count cherries only once
25 int currentCherries = grid[row][col1] + (col1 == col2 ? 0 : grid[row][col2]);
26
27 // Check all possible previous positions for robot 1 (can move diagonally down-left, down, or down-right)
28 for (int prevCol1 = col1 - 1; prevCol1 <= col1 + 1; ++prevCol1) {
29 // Check all possible previous positions for robot 2
30 for (int prevCol2 = col2 - 1; prevCol2 <= col2 + 1; ++prevCol2) {
31 // Validate previous positions are within bounds and reachable
32 if (prevCol1 >= 0 && prevCol1 < cols &&
33 prevCol2 >= 0 && prevCol2 < cols &&
34 dp[row - 1][prevCol1][prevCol2] != -1) {
35 // Update maximum cherries for current state
36 dp[row][col1][col2] = max(dp[row][col1][col2],
37 dp[row - 1][prevCol1][prevCol2] + currentCherries);
38 }
39 }
40 }
41 }
42 }
43 }
44
45 // Find the maximum cherries collected among all possible ending positions
46 int maxCherries = 0;
47 for (int col1 = 0; col1 < cols; ++col1) {
48 for (int col2 = 0; col2 < cols; ++col2) {
49 maxCherries = max(maxCherries, dp[rows - 1][col1][col2]);
50 }
51 }
52
53 return maxCherries;
54 }
55};
56
1/**
2 * Calculates maximum cherries that can be collected by two robots moving from top to bottom
3 * @param grid - 2D array representing the cherry field
4 * @returns Maximum number of cherries that can be collected
5 */
6function cherryPickup(grid: number[][]): number {
7 const rows: number = grid.length;
8 const cols: number = grid[0].length;
9
10 // Initialize 3D DP array: dp[row][robot1Col][robot2Col]
11 // dp[i][j1][j2] represents max cherries collected when robot1 is at (i, j1) and robot2 is at (i, j2)
12 const dp: number[][][] = Array.from({ length: rows }, () =>
13 Array.from({ length: cols }, () =>
14 Array.from({ length: cols }, () => -1)
15 )
16 );
17
18 // Initial state: robot1 starts at top-left (0, 0), robot2 starts at top-right (0, cols-1)
19 dp[0][0][cols - 1] = grid[0][0] + grid[0][cols - 1];
20
21 // Process each row from top to bottom
22 for (let row = 1; row < rows; ++row) {
23 // Try all possible positions for robot1 in current row
24 for (let robot1Col = 0; robot1Col < cols; ++robot1Col) {
25 // Try all possible positions for robot2 in current row
26 for (let robot2Col = 0; robot2Col < cols; ++robot2Col) {
27 // Calculate cherries collected at current positions
28 // If both robots are at same position, count cherries only once
29 const currentCherries: number = grid[row][robot1Col] +
30 (robot1Col === robot2Col ? 0 : grid[row][robot2Col]);
31
32 // Check all possible previous positions for robot1 (can move diagonally: left, down, right)
33 for (let prevRobot1Col = robot1Col - 1; prevRobot1Col <= robot1Col + 1; ++prevRobot1Col) {
34 // Check all possible previous positions for robot2
35 for (let prevRobot2Col = robot2Col - 1; prevRobot2Col <= robot2Col + 1; ++prevRobot2Col) {
36 // Validate previous positions are within bounds and reachable
37 if (prevRobot1Col >= 0 && prevRobot1Col < cols &&
38 prevRobot2Col >= 0 && prevRobot2Col < cols &&
39 dp[row - 1][prevRobot1Col][prevRobot2Col] !== -1) {
40
41 // Update maximum cherries for current state
42 dp[row][robot1Col][robot2Col] = Math.max(
43 dp[row][robot1Col][robot2Col],
44 dp[row - 1][prevRobot1Col][prevRobot2Col] + currentCherries
45 );
46 }
47 }
48 }
49 }
50 }
51 }
52
53 // Find maximum cherries collected in the last row across all valid ending positions
54 return Math.max(...dp[rows - 1].flat());
55}
56
Time and Space Complexity
Time Complexity: O(m × n²)
The algorithm uses dynamic programming with three nested loops:
- The outermost loop iterates through all rows:
O(m)
- The second and third loops iterate through all possible positions for robot 1 and robot 2 in each row:
O(n²)
- Inside these loops, there are two more nested loops checking previous positions (up to 3 positions each for both robots), but these are constant operations:
O(3 × 3) = O(9) = O(1)
- The final operation finds the maximum value in the last row, which requires checking all
n²
combinations:O(n²)
Therefore, the overall time complexity is O(m × n² × 1) + O(n²) = O(m × n²)
Space Complexity: O(m × n²)
The algorithm creates a 3D DP array f
with dimensions:
- First dimension (rows):
m
- Second dimension (robot 1 column positions):
n
- Third dimension (robot 2 column positions):
n
This results in a total space requirement of m × n × n = m × n²
Therefore, the space complexity is O(m × n²)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Handling of Same Cell Occupation
Pitfall: A common mistake is double-counting cherries when both robots occupy the same cell. Many solutions incorrectly add grid[row][col1] + grid[row][col2]
without checking if col1 == col2
.
Wrong approach:
# This always adds both values, causing double-counting current_cherries = grid[row][col1] + grid[row][col2]
Correct approach:
# Only count cherries once when robots are at the same position current_cherries = grid[row][col1] if col1 != col2: current_cherries += grid[row][col2]
2. Inefficient State Representation
Pitfall: Using a 4D DP array dp[row1][col1][row2][col2]
to track both robots' positions independently. Since both robots move simultaneously row by row, they're always in the same row.
Wrong approach:
# Unnecessary 4D array - wastes memory and complicates logic
dp = [[[[-1] * cols for _ in range(rows)] for _ in range(cols)] for _ in range(rows)]
Correct approach:
# 3D array is sufficient since row1 == row2 always
dp = [[[-1] * cols for _ in range(cols)] for _ in range(rows)]
3. Boundary Check Errors
Pitfall: Forgetting to validate that previous positions are within grid boundaries when checking state transitions, leading to index out of bounds errors.
Wrong approach:
# No boundary checking - will crash if prev_col < 0 or prev_col >= cols for prev_col1 in [col1 - 1, col1, col1 + 1]: for prev_col2 in [col2 - 1, col2, col2 + 1]: if dp[row - 1][prev_col1][prev_col2] != -1: # IndexError possible! # update logic
Correct approach:
for prev_col1 in range(col1 - 1, col1 + 2):
for prev_col2 in range(col2 - 1, col2 + 2):
if (0 <= prev_col1 < cols and
0 <= prev_col2 < cols and
dp[row - 1][prev_col1][prev_col2] != -1):
# update logic
4. Incorrect Base Case Initialization
Pitfall: Initializing the wrong starting positions or forgetting that robots might start at the same cell in edge cases (when grid has only 1 column).
Wrong approach:
# Doesn't handle single column grid correctly dp[0][0][cols - 1] = grid[0][0] + grid[0][cols - 1]
Correct approach:
# Handle the case where grid has only one column if cols == 1: dp[0][0][0] = grid[0][0] # Both robots at same cell else: dp[0][0][cols - 1] = grid[0][0] + grid[0][cols - 1]
5. Finding Maximum in Final Row
Pitfall: Only checking dp[rows-1][0][cols-1]
as the final answer, assuming robots must end at specific positions. The robots can end at any valid column positions in the last row.
Wrong approach:
# Assumes robots must end at specific positions return dp[rows - 1][0][cols - 1]
Correct approach:
# Check all possible ending positions
max_cherries = 0
for col1 in range(cols):
for col2 in range(cols):
if dp[rows - 1][col1][col2] != -1:
max_cherries = max(max_cherries, dp[rows - 1][col1][col2])
return max_cherries
Which two pointer techniques do you use to check if a string is a palindrome?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!