Close Menu
    Code and Debug
    • Home
    • Our Courses
    • Blog
    • About Us
    • Contact Us
    Facebook X (Twitter) Instagram YouTube WhatsApp
    • Home
    • Our Courses
    • Blog
    • About Us
    • Contact Us
    Facebook Instagram YouTube LinkedIn WhatsApp
    Code and Debug – BlogCode and Debug – Blog
    Code and Debug – BlogCode and Debug – Blog
    Home»Data Structures & Algorithms»Cherry Pickup II | Leetcode 1463 | Solved using Dynamic Programming
    Data Structures & Algorithms

    Cherry Pickup II | Leetcode 1463 | Solved using Dynamic Programming

    Anirudh KhuranaBy Anirudh Khurana11 August 2025No Comments9 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    Featured image of a question to solve cherry pickup 2
    Share
    Facebook Twitter LinkedIn Pinterest Email

    You are given a rows x cols matrix grid representing a field of cherries where grid[i][j] represents the number of cherries that you can collect from the (i, j) cell.

    Here’s the [Problem Link] to begin with.

    You have two robots that can collect cherries for you:

    • Robot #1 is located at the top-left corner (0, 0), and
    • Robot #2 is located at the top-right corner (0, cols - 1).

    Return the maximum number of cherries collection using both robots by following the rules below:

    • From a cell (i, j), robots can move to cell (i + 1, j - 1), (i + 1, j), or (i + 1, j + 1).
    • When any robot passes through a cell, It picks up all cherries, and the cell becomes an empty cell.
    • When both robots stay in the same cell, only one takes the cherries.
    • Both robots cannot move outside of the grid at any moment.
    • Both robots should reach the bottom row in grid.

    Example 1:

    Input: grid = [[3,1,1],[2,5,1],[1,5,5],[2,1,1]]
    Output: 24
    Explanation: Path of robot #1 and #2 are described in color green and blue respectively.
    Cherries taken by Robot #1, (3 + 2 + 5 + 2) = 12.
    Cherries taken by Robot #2, (1 + 5 + 5 + 1) = 12.
    Total of cherries: 12 + 12 = 24.

    Example 2:

    Input: grid = [[1,0,0,0,0,0,1],[2,0,0,0,0,3,0],[2,0,9,0,0,0,0],[0,3,0,5,4,0,0],[1,0,2,3,0,0,6]]
    Output: 28
    Explanation: Path of robot #1 and #2 are described in color green and blue respectively.
    Cherries taken by Robot #1, (1 + 9 + 5 + 2) = 17.
    Cherries taken by Robot #2, (1 + 3 + 4 + 3) = 11.
    Total of cherries: 17 + 11 = 28.

    Constraints:

    • rows == grid.length
    • cols == grid[i].length
    • 2 <= rows, cols <= 70
    • 0 <= grid[i][j] <= 100
    Contents:
     [show]
    • Solution 1: Recursion (Brute Force)
      • Intuition
      • Detailed Approach
      • Code
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Solution 2: Memoization (Top-Down DP)
      • Intuition
      • Detailed Approach
      • Code
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Solution 3: Tabulation (Bottom-Up DP)
      • Intuition
      • Detailed Approach
      • Code
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Solution 4: Tabulation (Space Optimization)
      • Intuition
      • Detailed Approach
      • Code
      • Code Explanation
      • Time and Space Complexity
      • Conclusion
    • Final Notes and Tips

    Solution 1: Recursion (Brute Force)

    Intuition

    Model the process as both robots moving row by row. At each row i, robot 1 can move to j1 + {-1, 0, +1}, and robot 2 to j2 + {-1, 0, +1}. For each pair of moves (3 x 3 = 9 combinations), compute cherries collected on current row and add the maximum of the recursive results for the next row. If both robots are on the same cell, count grid[i][j1] once; otherwise, sum grid[i][j1] + grid[i][j2]. Out-of-bound moves yield negative infinity to invalidate those paths.

    Detailed Approach

    • Base conditions:
      • If any column is out of bounds, return -inf to invalidate.
      • If at the last row, return grid[i][j1] if j1 == j2 else grid[i][j1] + grid[i][j2].
    • Transition:
      • Try all 9 moves for (new_j1, new_j2) in {-1, 0, +1} x {-1, 0, +1}, accumulate current cherries, and take the maximum.

    Code

    class Solution:
        def solve(self, i, j1, j2, grid):
            # If any robot goes out of bounds, path is invalid
            if j1 < 0 or j1 >= len(grid[0]) or j2 < 0 or j2 >= len(grid):
                return float("-inf")
            # If last row: collect once if same cell else sum both
            if i == len(grid) - 1:
                if j1 == j2:
                    return grid[i][j1]
                return grid[i][j1] + grid[i][j2]
            maxi = 0
            # Explore all 9 move combinations for both robots
            for new_j1 in range(-1, 2):
                for new_j2 in range(-1, 2):
                    if j1 == j2:
                        ans = grid[i][j1] + self.solve(
                            i + 1, j1 + new_j1, j2 + new_j2, grid
                        )
                    else:
                        ans = (
                            grid[i][j1]
                            + grid[i][j2]
                            + self.solve(i + 1, j1 + new_j1, j2 + new_j2, grid)
                        )
                    maxi = max(maxi, ans)
            return maxi
    
        def cherryPickup(self, grid: List[List[int]]) -> int:
            # Start with robot1 at col 0 and robot2 at col m-1
            return self.solve(0, 0, len(grid) - 1, grid)

    Code Explanation

    • The recursive function explores all combinations of moves for both robots.
    • It ensures invalid moves are discarded and correctly handles the “same cell” case.
    • The main function starts from the top row with robots at columns 0 and m-1.

    Time and Space Complexity

    • Precise: Time O(9^(n)) in worst case due to branching over 9 moves per row; Space O(n) for recursion depth.
    • Simplified: Exponential time; linear recursion space.

    Conclusion

    This gives the right answer but is impractical for larger n and m due to heavy recomputation.


    Solution 2: Memoization (Top-Down DP)

    Intuition

    The recursive state repeats for the same (i, j1, j2). Cache the maximum cherries for each state in a 3D DP table dp[i][j1][j2]. This reduces the complexity dramatically since each state is computed once.

    Detailed Approach

    • Use dp with dimensions n x m x m initialized to -1.
    • Before computing a state, return dp[i][j1][j2] if it’s already known.
    • Otherwise, compute as in recursion, store the best in dp[i][j1][j2], and return it.

    Code

    class Solution:
        def solve(self, i, j1, j2, grid, dp):
            # Boundary checks
            if j1 < 0 or j1 >= len(grid) or j2 < 0 or j2 >= len(grid):
                return float("-inf")
            # Base case at last row
            if i == len(grid) - 1:
                if j1 == j2:
                    return grid[i][j1]
                return grid[i][j1] + grid[i][j2]
            # Return cached result if computed
            if dp[i][j1][j2] != -1:
                return dp[i][j1][j2]
            maxi = 0
            # Try all move pairs
            for new_j1 in range(-1, 2):
                for new_j2 in range(-1, 2):
                    if j1 == j2:
                        ans = grid[i][j1] + self.solve(
                            i + 1, j1 + new_j1, j2 + new_j2, grid, dp
                        )
                    else:
                        ans = (
                            grid[i][j1]
                            + grid[i][j2]
                            + self.solve(i + 1, j1 + new_j1, j2 + new_j2, grid, dp)
                        )
                    maxi = max(maxi, ans)
            dp[i][j1][j2] = maxi
            return dp[i][j1][j2]
    
        def cherryPickup(self, grid: List[List[int]]) -> int:
            n = len(grid)
            m = len(grid)
            # DP dimensions: N x M x M
            dp = [[[-1 for _ in range(m)] for _ in range(m)] for _ in range(n)]
            return self.solve(0, 0, len(grid) - 1, grid, dp)

    Code Explanation

    • The memo table stores the best result for every (row, col1, col2).
    • This eliminates overlapping subproblems and makes the solution efficient.

    Time and Space Complexity

    • Precise: Time O(n × m × m × 9) ≈ O(n × m²); Space O(n × m²) for dp plus O(n) recursion stack.
    • Simplified: Time O(n m²), Space O(n m²).

    Conclusion

    Memoization turns the exponential recursion into a polynomial-time solution suitable for constraints up to 70.


    Solution 3: Tabulation (Bottom-Up DP)

    Intuition

    Build dp from the last row upward. For each row i and column pair (j1, j2), compute the best by considering all next moves (9 options) using the values from row i+1. Initialize the base row directly from the grid.

    Detailed Approach

    • Define dp[i][j1][j2] = maximum cherries from row i when robots are at (j1, j2).
    • Base: For i = n-1, dp[n-1][j1][j2] = grid[n-1][j1] if j1 == j2 else grid[n-1][j1] + grid[n-1][j2].
    • Transition: For each i from n-2 down to 0, and each j1, j2, compute max over valid next positions using dp[i+1][…].

    Code

    class Solution:
        def cherryPickup(self, grid: List[List[int]]) -> int:
            n = len(grid)
            m = len(grid)
            dp = [[[-1 for _ in range(m)] for _ in range(m)] for _ in range(n)]
            # Base cases for last row
            for j1 in range(m):
                for j2 in range(m):
                    if j1 == j2:
                        dp[n - 1][j1][j2] = grid[n - 1][j1]
                    else:
                        dp[n - 1][j1][j2] = grid[n - 1][j1] + grid[n - 1][j2]
            # Fill from bottom to top
            for i in range(n - 2, -1, -1):
                for j1 in range(m):
                    for j2 in range(m):
                        maxi = 0
                        for new_j1 in range(-1, 2):
                            for new_j2 in range(-1, 2):
                                if (
                                    j1 + new_j1 < 0
                                    or j2 + new_j2 < 0
                                    or j1 + new_j1 >= m
                                    or j2 + new_j2 >= m
                                ):
                                    ans = float("-inf")
                                elif j1 == j2:
                                    ans = grid[i][j1] + dp[i + 1][j1 + new_j1][j2 + new_j2]
                                else:
                                    ans = (
                                        grid[i][j1]
                                        + grid[i][j2]
                                        + dp[i + 1][j1 + new_j1][j2 + new_j2]
                                    )
                                maxi = max(maxi, ans)
                        dp[i][j1][j2] = maxi
            return dp[0][0][m - 1]

    Code Explanation

    • Initializes the last row where only immediate cherries matter.
    • For each upper row, aggregates the best future gains from the next row by exploring 9 possibilities.
    • The final answer is dp[m-1], matching the start positions.

    Time and Space Complexity

    • Precise: Time O(n × m × m × 9) ≈ O(n × m²); Space O(n × m²).
    • Simplified: Time O(n m²), Space O(n m²).

    Conclusion

    This bottom-up approach avoids recursion and is easy to iterate and debug.


    Solution 4: Tabulation (Space Optimization)

    Intuition

    At row i, you only need results from row i+1. Replace the 3D dp with two 2D layers: prev for row i+1 and curr for row i. Roll these layers as you move up to save memory from O(n m²) to O(m²).

    Detailed Approach

    • Initialize prev as the base row (n-1).
    • For each upper row, compute curr[j1][j2] using prev for next positions.
    • After finishing a row, set prev = curr and continue upward.
    • Final answer is in prev[m-1] after processing row 0.

    Code

    class Solution:
        def cherryPickup(self, grid: List[List[int]]) -> int:
            n = len(grid)
            m = len(grid)
            # Base: last row layer
            prev = [[-1 for _ in range(m)] for _ in range(m)]
            for j1 in range(m):
                for j2 in range(m):
                    if j1 == j2:
                        prev[j1][j2] = grid[n - 1][j1]
                    else:
                        prev[j1][j2] = grid[n - 1][j1] + grid[n - 1][j2]
            # Build upwards using rolling 2D arrays
            for i in range(n - 2, -1, -1):
                curr = [[-1 for _ in range(m)] for _ in range(m)]
                for j1 in range(m):
                    for j2 in range(m):
                        maxi = 0
                        for new_j1 in range(-1, 2):
                            for new_j2 in range(-1, 2):
                                if (
                                    j1 + new_j1 < 0
                                    or j2 + new_j2 < 0
                                    or j1 + new_j1 >= m
                                    or j2 + new_j2 >= m
                                ):
                                    ans = float("-inf")
                                elif j1 == j2:
                                    ans = grid[i][j1] + prev[j1 + new_j1][j2 + new_j2]
                                else:
                                    ans = (
                                        grid[i][j1]
                                        + grid[i][j2]
                                        + prev[j1 + new_j1][j2 + new_j2]
                                    )
                                maxi = max(maxi, ans)
                        curr[j1][j2] = maxi
                prev = curr
            return prev[m - 1]

    Code Explanation

    • Stores only two layers at any time: the current row’s DP and the next row’s DP.
    • Uses the same transition logic as tabulation but with reduced memory.

    Time and Space Complexity

    • Precise: Time O(n × m × m × 9) ≈ O(n × m²); Space O(m²) for two 2D layers.
    • Simplified: Time O(n m²), Space O(m²).

    Conclusion

    This is the interview-ready optimal DP: minimal space with the same time complexity and clean rolling-state logic.


    Final Notes and Tips

    • Always handle the “same cell” case by counting cherries once.
    • Use -inf for invalid moves to naturally exclude them from max computations.
    • The 3D state (i, j1, j2) encodes both robots’ positions; transitions are the 9 combined moves per step.
    • For constraints up to 70×70, O(n m²) with memoization or tabulation runs comfortably.
    Join our Advance DSA COURSE

    For any changes to the article, kindly email at code@codeanddebug.in or contact us at +91-9712928220.

    Dynamic Programming on 2D Arrays Hard
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleTrapping Rain Water | Leetcode 42 | Brute, Better and Optimal Solution using Stack
    Next Article Subset Sum Problem | Solved using Dynamic Programming
    Anirudh Khurana
    • Website

    Anirudh Khurana is the founder of Code and Debug, an Indian EdTech platform that helps students and working professionals crack coding interviews and move into data and AI roles. He teaches DSA, Python, SQL, data engineering and machine learning through live bootcamps and his YouTube channel, Code and Debug, which has over 50,000 subscribers. Every guide on this blog is written or reviewed by him and tested with real code.

    Related Posts

    Data Structures & Algorithms

    DSA Interview Cheat Sheet: All Patterns You Need to Know

    4 April 2026
    Data Structures & Algorithms

    Symmetric Tree | Leetcode 101 | Recursive DFS Approach

    2 September 2025
    Data Structures & Algorithms

    Binary Tree Right Side View | Leetcode 199 | BFS and DFS Approaches

    2 September 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Categories
    • AI & Machine Learning (4)
    • Data Structures & Algorithms (241)
      • Beginner (82)
      • Expert (52)
      • Intermediate (106)
    • Pandas (1)
    • Python (1)
    • Resume (1)
    • SQL (1)
    Recent Posts

    Confusion Matrix in Machine Learning: An Interactive Guide

    21 October 2026

    100 SQL Interview Questions with Queries and Answers (Freshers to Experienced)

    17 October 2026

    Logistic Regression in Machine Learning: Explained Visually (with Python)

    17 October 2026

    Linear Regression in Machine Learning: Explained Visually (with Python)

    17 October 2026

    100 Python Interview Questions and Answers for Freshers and Experienced (Free PDF)

    17 October 2026
    Facebook Instagram YouTube LinkedIn WhatsApp
    © 2025 Code and Debug. All rights reserved.

    Type above and press Enter to search. Press Esc to cancel.