Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1463. Cherry Pickup II - Leetcode Solution

Code Implementation

class Solution:
    def cherryPickup(self, grid):
        from functools import lru_cache
        rows, cols = len(grid), len(grid[0])

        @lru_cache(None)
        def dp(r, c1, c2):
            if c1 < 0 or c1 >= cols or c2 < 0 or c2 >= cols:
                return float('-inf')
            result = grid[r][c1]
            if c1 != c2:
                result += grid[r][c2]
            if r != rows - 1:
                temp = 0
                for d1 in (-1, 0, 1):
                    for d2 in (-1, 0, 1):
                        temp = max(temp, dp(r+1, c1+d1, c2+d2))
                result += temp
            return result

        return dp(0, 0, cols-1)
      
class Solution {
public:
    int cherryPickup(vector<vector<int>>& grid) {
        int rows = grid.size(), cols = grid[0].size();
        vector<vector<vector<int>>> dp(rows, vector<vector<int>>(cols, vector<int>(cols, -1)));

        function<int(int, int, int)> dfs = [&](int r, int c1, int c2) {
            if (c1 < 0 || c1 >= cols || c2 < 0 || c2 >= cols) return INT_MIN;
            if (dp[r][c1][c2] != -1) return dp[r][c1][c2];
            int res = grid[r][c1];
            if (c1 != c2) res += grid[r][c2];
            if (r != rows - 1) {
                int temp = 0;
                for (int d1 = -1; d1 <= 1; ++d1) {
                    for (int d2 = -1; d2 <= 1; ++d2) {
                        temp = max(temp, dfs(r+1, c1+d1, c2+d2));
                    }
                }
                res += temp;
            }
            return dp[r][c1][c2] = res;
        };

        return dfs(0, 0, cols-1);
    }
};
      
class Solution {
    public int cherryPickup(int[][] grid) {
        int rows = grid.length, cols = grid[0].length;
        Integer[][][] dp = new Integer[rows][cols][cols];

        return dfs(grid, 0, 0, cols - 1, dp);
    }

    private int dfs(int[][] grid, int r, int c1, int c2, Integer[][][] dp) {
        int cols = grid[0].length, rows = grid.length;
        if (c1 < 0 || c1 >= cols || c2 < 0 || c2 >= cols) return Integer.MIN_VALUE;
        if (dp[r][c1][c2] != null) return dp[r][c1][c2];
        int res = grid[r][c1];
        if (c1 != c2) res += grid[r][c2];
        if (r != rows - 1) {
            int temp = 0;
            for (int d1 = -1; d1 <= 1; d1++) {
                for (int d2 = -1; d2 <= 1; d2++) {
                    temp = Math.max(temp, dfs(grid, r+1, c1+d1, c2+d2, dp));
                }
            }
            res += temp;
        }
        dp[r][c1][c2] = res;
        return res;
    }
}
      
var cherryPickup = function(grid) {
    const rows = grid.length, cols = grid[0].length;
    const memo = new Map();

    function dp(r, c1, c2) {
        if (c1 < 0 || c1 >= cols || c2 < 0 || c2 >= cols) return -Infinity;
        const key = r + ',' + c1 + ',' + c2;
        if (memo.has(key)) return memo.get(key);

        let result = grid[r][c1];
        if (c1 !== c2) result += grid[r][c2];
        if (r !== rows - 1) {
            let temp = 0;
            for (let d1 = -1; d1 <= 1; d1++) {
                for (let d2 = -1; d2 <= 1; d2++) {
                    temp = Math.max(temp, dp(r+1, c1+d1, c2+d2));
                }
            }
            result += temp;
        }
        memo.set(key, result);
        return result;
    }

    return dp(0, 0, cols-1);
};
      

Problem Description

You are given a 2D grid of integers called grid where each cell represents the number of cherries at that position. Two robots start at the top row: one at the leftmost column (column 0) and the other at the rightmost column (column n-1). On each move, both robots move to the next row simultaneously, and each robot can move to the same column, or one column left or right (i.e., from column c, a robot can move to c-1, c, or c+1 in the next row).

When a robot visits a cell, it picks up all cherries from that cell. If both robots land on the same cell, only one can pick up the cherries (the cherries are counted only once).

The goal is to collect the maximum number of cherries possible as both robots move from the top to the bottom row. Each robot must move down to the next row in every step, and both must stay within the grid boundaries.

Key constraints:

  • Each robot can only move to adjacent columns or stay in the same column (per move).
  • Both robots start at fixed positions: columns 0 and n-1 of the top row.
  • If both robots land on the same cell, cherries are only counted once for that cell.
  • Each cell's cherries can only be picked up once per visit.

Thought Process

The first idea that comes to mind is to simulate every possible path both robots can take from the top to the bottom, keeping track of the cherries collected. However, since each robot has 3 possible moves at every step (left, stay, right), and there are two robots, the number of possibilities grows exponentially with the number of rows. This brute-force approach quickly becomes infeasible for large grids.

We need a way to avoid recalculating results for the same positions. This is a classic scenario for dynamic programming (DP) with memoization. By storing the results for specific states (i.e., the row and columns of both robots), we can reuse them whenever we reach the same configuration again.

The core insight is to represent the state by three variables: the current row, and the columns of both robots. For each such state, we can recursively compute the maximum cherries that can be collected from that point onward, and store the result to avoid duplicate work.

Solution Approach

  • State Representation:
    • We define a DP function dp(r, c1, c2) where r is the current row, and c1 and c2 are the current columns of robot 1 and robot 2, respectively.
    • The function returns the maximum cherries that can be collected starting from this state.
  • Base Cases:
    • If either robot is out of bounds (column index is less than 0 or greater than or equal to the number of columns), return negative infinity (invalid path).
    • If we reach the last row, return the cherries at the robots' positions (making sure not to double-count if both are on the same cell).
  • Recursive Step:
    • For each possible move (left, stay, right) for both robots, recursively call dp for the next row and new columns.
    • Collect the cherries at the current positions for both robots (again, avoid double-counting if they overlap).
    • Add the maximum cherries from all possible next moves to the cherries collected at the current step.
  • Memoization:
    • Store the result of each dp(r, c1, c2) call in a memoization table or cache to avoid redundant calculations.
  • Initialization:
    • Start the process from dp(0, 0, n-1), representing both robots at the top row in their respective starting columns.

This approach ensures that we explore all valid paths in an efficient manner, leveraging overlapping subproblems and optimal substructure properties of dynamic programming.

Example Walkthrough

Let's consider a simple example:

    grid = [
      [3, 1, 1],
      [2, 5, 1],
      [1, 5, 5],
      [2, 1, 1]
    ]
  
  1. Start: Robot 1 at (0,0), Robot 2 at (0,2). Cherries collected: 3 (robot 1) + 1 (robot 2) = 4.
  2. First Move: For each robot, try all combinations of moves (left, stay, right) to get to the next row. For example:
    • Robot 1 moves to (1,0), Robot 2 moves to (1,1): cherries = 2 + 5 = 7 (plus previous 4 = 11).
    • Robot 1 moves to (1,1), Robot 2 moves to (1,2): cherries = 5 + 1 = 6 (plus previous 4 = 10).
    • And so on for all valid combinations.
  3. Continue: Repeat this process for each subsequent row, always choosing the move that maximizes the total cherries, while ensuring robots stay within bounds and don't double-count cherries if they land on the same cell.
  4. Final Result: The DP process finds the path where the robots collect the most cherries, which for this grid is 24.

The DP table ensures that repeated subproblems (same row and columns for both robots) are only solved once.

Time and Space Complexity

  • Brute-Force Approach:
    • For each row, each robot has 3 possible moves, so for m rows, there are 3^m possibilities per robot, or 9^m total combinations. This is infeasible for large grids.
  • Optimized DP Approach:
    • There are rows rows and cols columns for each robot, so the number of unique states is rows * cols * cols.
    • For each state, we check up to 9 combinations (3 moves for each robot).
    • Time Complexity: O(rows * cols^2 * 9) simplifies to O(rows * cols^2) since 9 is a constant.
    • Space Complexity: O(rows * cols^2) for the memoization table.

Summary

The "Cherry Pickup II" problem is a classic example of dynamic programming with multiple agents. By representing the state with the current row and columns of both robots, and using memoization to store results of subproblems, we avoid redundant computation and efficiently solve the problem. The key insights are to avoid double-counting cherries when both robots land on the same cell and to explore all possible moves at each step. This DP approach transforms an exponential brute-force problem into a polynomial-time solution, making it both elegant and practical.