🧠
Memoization (Top-Down DP)
💡 Memoization caches results of recursive calls to avoid recomputation, drastically improving performance while keeping the recursive structure.
Intuition
Store results for each state (positions of both players) so that repeated calls return cached results instead of recomputing.
Algorithm
- Use a 3D cache keyed by (r1, c1, r2) to store results.
- Before recursive calls, check if result is cached and return if so.
- Perform same recursive exploration as brute force.
- Cache and return the computed result.
- Return max cherries or 0 if no valid path.
💡 Memoization avoids exponential recomputation by remembering results of states already solved.
Recurrence:dp[r1][c1][r2] = max of dp from previous positions + cherries at current positions (count once if same cell)
from typing import List
import sys
sys.setrecursionlimit(10**7)
def cherryPickup(grid: List[List[int]]) -> int:
n = len(grid)
memo = {}
def dfs(r1, c1, r2):
c2 = r1 + c1 - r2
if r1 >= n or c1 >= n or r2 >= n or c2 >= n or grid[r1][c1] == -1 or grid[r2][c2] == -1:
return float('-inf')
if r1 == n - 1 and c1 == n - 1:
return grid[r1][c1]
if (r1, c1, r2) in memo:
return memo[(r1, c1, r2)]
res = grid[r1][c1]
if r1 != r2 or c1 != c2:
res += grid[r2][c2]
temp = max(
dfs(r1 + 1, c1, r2 + 1),
dfs(r1, c1 + 1, r2),
dfs(r1 + 1, c1, r2),
dfs(r1, c1 + 1, r2 + 1)
)
res += temp
memo[(r1, c1, r2)] = res
return res
ans = dfs(0, 0, 0)
return max(ans, 0)
# Driver code
if __name__ == '__main__':
grid = [[0,1,-1],[1,0,-1],[1,1,1]]
print(cherryPickup(grid)) # Expected output: 5
Line Notes
memo = {}Initialize cache to store results of states
if (r1, c1, r2) in memo:Return cached result if available to avoid recomputation
memo[(r1, c1, r2)] = resStore computed result for current state
def dfs(r1, c1, r2):Recursive function with memoization
temp = max(...)Explore all four next moves recursively
return max(ans, 0)Return max cherries or 0 if no valid path
import java.util.*;
public class CherryPickup {
private int n;
private int[][] grid;
private Map<String, Integer> memo = new HashMap<>();
public int cherryPickup(int[][] grid) {
this.n = grid.length;
this.grid = grid;
int res = dfs(0, 0, 0);
return Math.max(res, 0);
}
private int dfs(int r1, int c1, int r2) {
int c2 = r1 + c1 - r2;
if (r1 >= n || c1 >= n || r2 >= n || c2 >= n || grid[r1][c1] == -1 || grid[r2][c2] == -1) {
return Integer.MIN_VALUE;
}
if (r1 == n - 1 && c1 == n - 1) {
return grid[r1][c1];
}
String key = r1 + "," + c1 + "," + r2;
if (memo.containsKey(key)) {
return memo.get(key);
}
int res = grid[r1][c1];
if (r1 != r2 || c1 != c2) {
res += grid[r2][c2];
}
int temp = Math.max(
Math.max(dfs(r1 + 1, c1, r2 + 1), dfs(r1, c1 + 1, r2)),
Math.max(dfs(r1 + 1, c1, r2), dfs(r1, c1 + 1, r2 + 1))
);
res += temp;
memo.put(key, res);
return res;
}
public static void main(String[] args) {
CherryPickup cp = new CherryPickup();
int[][] grid = {{0,1,-1},{1,0,-1},{1,1,1}};
System.out.println(cp.cherryPickup(grid)); // Expected output: 5
}
}
Line Notes
private Map<String, Integer> memo = new HashMap<>();Cache results of states to avoid recomputation
String key = r1 + "," + c1 + "," + r2;Create unique key for memoization
if (memo.containsKey(key)) {Return cached result if available
memo.put(key, res);Store computed result
private int dfs(int r1, int c1, int r2) {Recursive function with memoization
int temp = Math.max(...);Explore all four next moves recursively
#include <bits/stdc++.h>
using namespace std;
class Solution {
int n;
vector<vector<int>> grid;
unordered_map<string, int> memo;
public:
int cherryPickup(vector<vector<int>>& grid) {
this->n = grid.size();
this->grid = grid;
int res = dfs(0, 0, 0);
return max(res, 0);
}
int dfs(int r1, int c1, int r2) {
int c2 = r1 + c1 - r2;
if (r1 >= n || c1 >= n || r2 >= n || c2 >= n || grid[r1][c1] == -1 || grid[r2][c2] == -1) {
return INT_MIN;
}
if (r1 == n - 1 && c1 == n - 1) {
return grid[r1][c1];
}
string key = to_string(r1) + "," + to_string(c1) + "," + to_string(r2);
if (memo.count(key)) {
return memo[key];
}
int res = grid[r1][c1];
if (r1 != r2 || c1 != c2) {
res += grid[r2][c2];
}
int temp = max({
dfs(r1 + 1, c1, r2 + 1),
dfs(r1, c1 + 1, r2),
dfs(r1 + 1, c1, r2),
dfs(r1, c1 + 1, r2 + 1)
});
res += temp;
memo[key] = res;
return res;
}
};
int main() {
Solution sol;
vector<vector<int>> grid = {{0,1,-1},{1,0,-1},{1,1,1}};
cout << sol.cherryPickup(grid) << endl; // Expected output: 5
return 0;
}
Line Notes
unordered_map<string, int> memo;Cache results of states to avoid recomputation
string key = to_string(r1) + "," + to_string(c1) + "," + to_string(r2);Create unique key for memoization
if (memo.count(key)) {Return cached result if available
memo[key] = res;Store computed result
int dfs(int r1, int c1, int r2) {Recursive function with memoization
int temp = max({ ... });Explore all four next moves recursively
function cherryPickup(grid) {
const n = grid.length;
const memo = new Map();
function dfs(r1, c1, r2) {
const c2 = r1 + c1 - r2;
if (r1 >= n || c1 >= n || r2 >= n || c2 >= n || grid[r1][c1] === -1 || grid[r2][c2] === -1) {
return Number.NEGATIVE_INFINITY;
}
if (r1 === n - 1 && c1 === n - 1) {
return grid[r1][c1];
}
const key = `${r1},${c1},${r2}`;
if (memo.has(key)) {
return memo.get(key);
}
let res = grid[r1][c1];
if (r1 !== r2 || c1 !== c2) {
res += grid[r2][c2];
}
const temp = Math.max(
dfs(r1 + 1, c1, r2 + 1),
dfs(r1, c1 + 1, r2),
dfs(r1 + 1, c1, r2),
dfs(r1, c1 + 1, r2 + 1)
);
res += temp;
memo.set(key, res);
return res;
}
const ans = dfs(0, 0, 0);
return Math.max(ans, 0);
}
// Test
const grid = [[0,1,-1],[1,0,-1],[1,1,1]];
console.log(cherryPickup(grid)); // Expected output: 5
Line Notes
const memo = new Map();Initialize cache to store results
const key = `${r1},${c1},${r2}`;Create unique key for memoization
if (memo.has(key)) {Return cached result if available
memo.set(key, res);Store computed result
function dfs(r1, c1, r2) {Recursive function with memoization
const temp = Math.max(...);Explore all four next moves recursively
TimeO(n^3)
SpaceO(n^3) for memoization and recursion stack
There are at most n^3 states (r1, c1, r2), each computed once.
💡 For n=50, this means up to 125,000 states, which is feasible with memoization.
Interview Verdict: Accepted
Memoization makes the solution efficient enough for the problem constraints.