🧠
Memoization with Recursion
💡 This approach uses recursion with memoization to avoid recomputing overlapping subproblems, improving efficiency over brute force.
Intuition
Use recursion to explore buy/sell decisions day by day, caching results to avoid repeated work.
Algorithm
- Define a recursive function with parameters: current day, number of transactions left, and holding state.
- At each day, decide to buy, sell, or skip based on current state.
- Use memoization to store results for states to avoid recomputation.
- Return the maximum profit achievable.
💡 This approach is easier to implement than tabulation but still may be slow for very large inputs.
Recurrence:dp(i, k, holding) = max(
dp(i+1, k, holding),
if holding == 0: -prices[i] + dp(i+1, k, 1),
else: prices[i] + dp(i+1, k-1, 0)
)
def maxProfit(prices):
from functools import lru_cache
n = len(prices)
@lru_cache(None)
def dfs(i, k, holding):
if i == n or k == 0:
return 0
do_nothing = dfs(i+1, k, holding)
if holding == 0:
buy = dfs(i+1, k, 1) - prices[i]
return max(do_nothing, buy)
else:
sell = dfs(i+1, k-1, 0) + prices[i]
return max(do_nothing, sell)
return dfs(0, 2, 0)
# Driver code
if __name__ == '__main__':
prices = [3,3,5,0,0,3,1,4]
print(maxProfit(prices)) # Expected output: 6
Line Notes
@lru_cache(None)Memoize results to avoid recomputing states, improving efficiency.
if i == n or k == 0:Base case: no days left or no transactions left means no profit.
do_nothing = dfs(i+1, k, holding)Option to skip current day without any action.
if holding == 0:If not holding stock, option to buy today.
buy = dfs(i+1, k, 1) - prices[i]Buy stock today, reducing cash by price.
sell = dfs(i+1, k-1, 0) + prices[i]Sell stock today, gain profit and reduce transactions left.
import java.util.*;
public class Solution {
private static int[] prices;
private static Integer[][][] memo;
public static int dfs(int i, int k, int holding) {
if (i == prices.length || k == 0) return 0;
if (memo[i][k][holding] != null) return memo[i][k][holding];
int doNothing = dfs(i + 1, k, holding);
int result;
if (holding == 0) {
int buy = dfs(i + 1, k, 1) - prices[i];
result = Math.max(doNothing, buy);
} else {
int sell = dfs(i + 1, k - 1, 0) + prices[i];
result = Math.max(doNothing, sell);
}
memo[i][k][holding] = result;
return result;
}
public static int maxProfit(int[] p) {
prices = p;
memo = new Integer[prices.length + 1][3][2];
return dfs(0, 2, 0);
}
public static void main(String[] args) {
int[] prices = {3,3,5,0,0,3,1,4};
System.out.println(maxProfit(prices)); // Expected output: 6
}
}
Line Notes
if (i == prices.length || k == 0) return 0;Base case: no days or no transactions left means no profit.
if (memo[i][k][holding] != null) return memo[i][k][holding];Return cached result if available to avoid recomputation.
int doNothing = dfs(i + 1, k, holding);Option to skip current day.
if (holding == 0)If not holding, option to buy today.
int buy = dfs(i + 1, k, 1) - prices[i];Buy stock today, reducing cash.
int sell = dfs(i + 1, k - 1, 0) + prices[i];Sell stock today, gain profit and reduce transactions.
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int prices[100005];
int memo[100005][3][2];
int n;
int dfs(int i, int k, int holding) {
if (i == n || k == 0) return 0;
if (memo[i][k][holding] != -1) return memo[i][k][holding];
int doNothing = dfs(i + 1, k, holding);
int result;
if (holding == 0) {
int buy = dfs(i + 1, k, 1) - prices[i];
result = max(doNothing, buy);
} else {
int sell = dfs(i + 1, k - 1, 0) + prices[i];
result = max(doNothing, sell);
}
memo[i][k][holding] = result;
return result;
}
int maxProfit(vector<int>& p) {
n = p.size();
for (int i = 0; i <= n; i++)
for (int j = 0; j <= 2; j++)
memo[i][j][0] = memo[i][j][1] = -1;
for (int i = 0; i < n; i++) prices[i] = p[i];
return dfs(0, 2, 0);
}
int main() {
vector<int> prices = {3,3,5,0,0,3,1,4};
cout << maxProfit(prices) << endl; // Expected output: 6
return 0;
}
Line Notes
if (i == n || k == 0) return 0;Base case: no days or no transactions left means no profit.
if (memo[i][k][holding] != -1) return memo[i][k][holding];Return cached result if computed to avoid recomputation.
int doNothing = dfs(i + 1, k, holding);Option to skip current day.
if (holding == 0)If not holding, option to buy today.
int buy = dfs(i + 1, k, 1) - prices[i];Buy stock today, reducing cash.
int sell = dfs(i + 1, k - 1, 0) + prices[i];Sell stock today, gain profit and reduce transactions.
function maxProfit(prices) {
const n = prices.length;
const memo = new Array(n).fill(0).map(() => new Array(3).fill(0).map(() => new Array(2).fill(null)));
function dfs(i, k, holding) {
if (i === n || k === 0) return 0;
if (memo[i][k][holding] !== null) return memo[i][k][holding];
const doNothing = dfs(i + 1, k, holding);
let result;
if (holding === 0) {
const buy = dfs(i + 1, k, 1) - prices[i];
result = Math.max(doNothing, buy);
} else {
const sell = dfs(i + 1, k - 1, 0) + prices[i];
result = Math.max(doNothing, sell);
}
memo[i][k][holding] = result;
return result;
}
return dfs(0, 2, 0);
}
// Test
console.log(maxProfit([3,3,5,0,0,3,1,4])); // Expected output: 6
Line Notes
if (i === n || k === 0) return 0;Base case: no days or no transactions left means no profit.
if (memo[i][k][holding] !== null) return memo[i][k][holding];Return cached result if available to avoid recomputation.
const doNothing = dfs(i + 1, k, holding);Option to skip current day.
if (holding === 0)If not holding, option to buy today.
const buy = dfs(i + 1, k, 1) - prices[i];Buy stock today, reducing cash.
const sell = dfs(i + 1, k - 1, 0) + prices[i];Sell stock today, gain profit and reduce transactions.
TimeO(n * k * 2) = O(n)
SpaceO(n * k * 2) for memoization stack
Each day, transaction count, and holding state is computed once and cached.
💡 For n=10^5, this is feasible with memoization, unlike brute force.
Interview Verdict: Accepted
This approach is efficient enough for large inputs and demonstrates DP with memoization well.