🧠
Top-Down DP with Memoization
💡 Memoization caches results of recursive calls to avoid recomputing states, drastically improving performance while keeping the recursive intuition.
Intuition
Store results of dfs(day, holding) in a cache so repeated calls return instantly, reducing exponential to linear time.
Algorithm
- Define recursive function with day and holding state.
- Use a memo dictionary keyed by (day, holding) to store results.
- If result cached, return it immediately.
- Otherwise compute as in brute force and cache before returning.
💡 Memoization turns exponential recursion into linear by remembering past results.
Recurrence:Same as brute force but with caching to avoid recomputation.
def maxProfit(prices):
n = len(prices)
memo = {}
def dfs(i, holding):
if i >= n:
return 0
if (i, holding) in memo:
return memo[(i, holding)]
if holding:
sell = prices[i] + dfs(i+2, False)
cooldown = dfs(i+1, True)
memo[(i, holding)] = max(sell, cooldown)
else:
buy = -prices[i] + dfs(i+1, True)
cooldown = dfs(i+1, False)
memo[(i, holding)] = max(buy, cooldown)
return memo[(i, holding)]
return dfs(0, False)
if __name__ == '__main__':
print(maxProfit([1,2,3,0,2])) # 3
Line Notes
memo = {}Initialize cache to store computed states
if (i, holding) in memo:Return cached result to avoid recomputation
memo[(i, holding)] = max(sell, cooldown)Cache computed result before returning
return dfs(0, False)Start recursion with memoization
import java.util.*;
public class Solution {
private Map<String, Integer> memo = new HashMap<>();
public int maxProfit(int[] prices) {
return dfs(prices, 0, false);
}
private int dfs(int[] prices, int i, boolean holding) {
if (i >= prices.length) return 0;
String key = i + "," + holding;
if (memo.containsKey(key)) return memo.get(key);
int res;
if (holding) {
int sell = prices[i] + dfs(prices, i + 2, false);
int cooldown = dfs(prices, i + 1, true);
res = Math.max(sell, cooldown);
} else {
int buy = -prices[i] + dfs(prices, i + 1, true);
int cooldown = dfs(prices, i + 1, false);
res = Math.max(buy, cooldown);
}
memo.put(key, res);
return res;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.maxProfit(new int[]{1,2,3,0,2})); // 3
}
}
Line Notes
private Map<String, Integer> memo = new HashMap<>();Cache to store results keyed by day and holding
if (memo.containsKey(key)) return memo.get(key);Return cached result if available
memo.put(key, res);Store computed result before returning
return dfs(prices, 0, false);Start memoized recursion
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
class Solution {
unordered_map<string,int> memo;
int dfs(const vector<int>& prices, int i, bool holding) {
if (i >= prices.size()) return 0;
string key = to_string(i) + "," + (holding ? "1" : "0");
if (memo.count(key)) return memo[key];
int res;
if (holding) {
int sell = prices[i] + dfs(prices, i + 2, false);
int cooldown = dfs(prices, i + 1, true);
res = max(sell, cooldown);
} else {
int buy = -prices[i] + dfs(prices, i + 1, true);
int cooldown = dfs(prices, i + 1, false);
res = max(buy, cooldown);
}
memo[key] = res;
return res;
}
public:
int maxProfit(vector<int>& prices) {
return dfs(prices, 0, false);
}
};
int main() {
Solution sol;
vector<int> prices = {1,2,3,0,2};
cout << sol.maxProfit(prices) << endl; // 3
return 0;
}
Line Notes
unordered_map<string,int> memo;Cache for memoization keyed by day and holding
if (memo.count(key)) return memo[key];Return cached result if present
memo[key] = res;Store computed result
return dfs(prices, 0, false);Start memoized recursion
var maxProfit = function(prices) {
const n = prices.length;
const memo = new Map();
function dfs(i, holding) {
if (i >= n) return 0;
const key = i + ',' + holding;
if (memo.has(key)) return memo.get(key);
let res;
if (holding) {
const sell = prices[i] + dfs(i + 2, false);
const cooldown = dfs(i + 1, true);
res = Math.max(sell, cooldown);
} else {
const buy = -prices[i] + dfs(i + 1, true);
const cooldown = dfs(i + 1, false);
res = Math.max(buy, cooldown);
}
memo.set(key, res);
return res;
}
return dfs(0, false);
};
console.log(maxProfit([1,2,3,0,2])); // 3
Line Notes
const memo = new Map();Cache to store computed states
if (memo.has(key)) return memo.get(key);Return cached result if available
memo.set(key, res);Cache computed result
return dfs(0, false);Start memoized recursion
TimeO(n) because each day and holding state is computed once
SpaceO(n) for recursion stack and memo storage
Memoization reduces exponential calls to linear by caching results for each (day, holding) pair
💡 For n=20, this means about 40 calls, a huge improvement over brute force.
Interview Verdict: Accepted
This approach is efficient enough for large inputs and retains recursive clarity.