101 Logo
onenoughtone

Solution Approach

Solution Overview

There are 3 main approaches to solve this problem:

  1. Dynamic Programming (3D) - Complex approach
  2. Dynamic Programming (2D) - Complex approach
  3. Dynamic Programming with Memoization - Complex approach

Approach 1: Dynamic Programming (3D)

The key insight for solving this problem is to move both robots simultaneously, row by row.

Let's define dp[row][col1][col2] as the maximum cherries that can be collected when robot 1 is at position (row, col1) and robot 2 is at position (row, col2).

For each state (row, col1, col2), we need to consider all possible transitions to the next row. Since each robot can move to one of three possible positions in the next row, there are a total of 9 possible transitions.

The recurrence relation is:

dp[row][col1][col2] = cherries + max(dp[row+1][new_col1][new_col2]) for all possible new_col1 and new_col2

where cherries is the number of cherries collected at the current state:

cherries = grid[row][col1] + (col1 != col2 ? grid[row][col2] : 0)

The base case is the bottom row, where we can directly calculate the cherries collected.

We can solve this problem using either a top-down approach (memoization) or a bottom-up approach (tabulation).

Algorithm:

  1. Initialize a 3D DP array dp of size rows × cols × cols, with all elements set to -1 (for memoization) or 0 (for tabulation).
  2. Define a recursive function dp(row, col1, col2) that returns the maximum cherries that can be collected when robot 1 is at position (row, col1) and robot 2 is at position (row, col2).
  3. Base case: If row == rows, return 0 (no more cherries to collect).
  4. Calculate the cherries collected at the current state: cherries = grid[row][col1] + (col1 != col2 ? grid[row][col2] : 0).
  5. For each possible move of robot 1 (col1 - 1, col1, col1 + 1) and robot 2 (col2 - 1, col2, col2 + 1):
  6. a. If the new positions are valid (0 <= new_col1, new_col2 < cols), calculate the maximum cherries that can be collected from the next row.
  7. b. Update the maximum cherries for the current state.
  8. Return dp[0][0][cols-1], which represents the maximum cherries that can be collected when robot 1 starts at (0, 0) and robot 2 starts at (0, cols-1).

Time Complexity:

O(rows * cols^2)

We need to compute the result for each state (row, col1, col2), and there are rows * cols * cols possible states. Each state takes O(1) time to compute.

Space Complexity:

O(rows * cols^2)

We use a 3D DP array of size rows * cols * cols to store the maximum cherries for each state.

Approach 2: Dynamic Programming (2D)

We can optimize the space complexity of the dynamic programming approach by using a 2D array instead of a 3D array.

The key observation is that when we're calculating dp[row][col1][col2], we only need the values from the next row dp[row+1][...][...]. So, we can use two 2D arrays, curr and next, to store the current and next rows' values, respectively.

The recurrence relation remains the same:

curr[col1][col2] = cherries + max(next[new_col1][new_col2]) for all possible new_col1 and new_col2

After processing each row, we swap curr and next to prepare for the next row.

This approach reduces the space complexity from O(rows * cols^2) to O(cols^2), while maintaining the same time complexity.

Algorithm:

  1. Initialize two 2D arrays curr and next of size cols × cols, with all elements set to 0.
  2. For the bottom row (row = rows - 1), calculate the cherries collected for all possible positions of both robots.
  3. For each row from rows - 2 down to 0:
  4. a. For each possible position of robot 1 (col1 from 0 to cols - 1) and robot 2 (col2 from 0 to cols - 1):
  5. i. Calculate the cherries collected at the current state: cherries = grid[row][col1] + (col1 != col2 ? grid[row][col2] : 0).
  6. ii. For each possible move of robot 1 (col1 - 1, col1, col1 + 1) and robot 2 (col2 - 1, col2, col2 + 1):
  7. 1. If the new positions are valid (0 <= new_col1, new_col2 < cols), calculate the maximum cherries that can be collected from the next row.
  8. 2. Update the maximum cherries for the current state.
  9. b. Swap curr and next to prepare for the next row.
  10. Return curr[0][cols-1], which represents the maximum cherries that can be collected when robot 1 starts at (0, 0) and robot 2 starts at (0, cols-1).

Time Complexity:

O(rows * cols^2)

We still need to process each state (row, col1, col2), which takes O(rows * cols^2) time.

Space Complexity:

O(cols^2)

We only use two 2D arrays of size cols * cols to store the maximum cherries for the current and next rows.

Approach 3: Dynamic Programming with Memoization

We can also solve this problem using a top-down approach with memoization, which is essentially a recursive solution with caching to avoid redundant calculations.

Let's define a recursive function dp(row, col1, col2) that returns the maximum cherries that can be collected when robot 1 is at position (row, col1) and robot 2 is at position (row, col2).

The recurrence relation is the same as in the bottom-up approach:

dp(row, col1, col2) = cherries + max(dp(row+1, new_col1, new_col2)) for all possible new_col1 and new_col2

To avoid redundant calculations, we use memoization to store the results of subproblems that we've already solved.

This approach is often easier to implement and understand, but it may have a higher constant factor in terms of time complexity due to the overhead of recursion.

Algorithm:

  1. Initialize a memoization table memo of size rows × cols × cols, with all elements set to -1.
  2. Define a recursive function dp(row, col1, col2) that returns the maximum cherries that can be collected when robot 1 is at position (row, col1) and robot 2 is at position (row, col2).
  3. Base case: If row == rows, return 0 (no more cherries to collect).
  4. If memo[row][col1][col2] != -1, return memo[row][col1][col2] (already computed).
  5. Calculate the cherries collected at the current state: cherries = grid[row][col1] + (col1 != col2 ? grid[row][col2] : 0).
  6. Initialize max_cherries = 0.
  7. For each possible move of robot 1 (col1 - 1, col1, col1 + 1) and robot 2 (col2 - 1, col2, col2 + 1):
  8. a. If the new positions are valid (0 <= new_col1, new_col2 < cols), calculate the maximum cherries that can be collected from the next row.
  9. b. Update max_cherries if a better result is found.
  10. Update memo[row][col1][col2] = cherries + max_cherries and return it.
  11. Call dp(0, 0, cols-1) to get the maximum cherries that can be collected.

Time Complexity:

O(rows * cols^2)

We need to compute the result for each state (row, col1, col2), and there are rows * cols * cols possible states. Each state takes O(1) time to compute.

Space Complexity:

O(rows * cols^2)

We use a memoization table of size rows * cols * cols to store the results of subproblems, and the recursion stack can go up to a depth of O(rows).

Pseudocode

solution.pseudo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function cherryPickup(grid):
rows = grid.length
cols = grid[0].length
// Initialize DP array
dp = new 3D array of size rows × cols × cols, initialized with -1
// Define recursive function
function dfs(row, col1, col2):
// Base case: reached the bottom row
if row == rows:
return 0
// Check if already computed
if dp[row][col1][col2] != -1:
return dp[row][col1][col2]
// Calculate cherries collected at current state
cherries = grid[row][col1]
if col1 != col2:
cherries += grid[row][col2]
// Initialize maximum cherries
max_cherries = 0
// Try all possible moves for both robots
for d1 in [-1, 0, 1]:
for d2 in [-1, 0, 1]:
new_col1 = col1 + d1
new_col2 = col2 + d2
// Check if new positions are valid
if 0 <= new_col1 < cols and 0 <= new_col2 < cols:
max_cherries = max(max_cherries, dfs(row + 1, new_col1, new_col2))
// Update DP array
dp[row][col1][col2] = cherries + max_cherries
return dp[row][col1][col2]
return dfs(0, 0, cols - 1)
ProblemSolutionCode
101 Logo
onenoughtone