Best Time to Buy and Sell Stock with Cooldown
Difficulty: Medium Source: NeetCode
Problem
You are given an array
priceswhereprices[i]is the price of a stock on dayi. You can buy and sell on multiple days, but you cannot buy on the next day after selling (cooldown of 1 day). You may not hold more than one stock at a time. Find the maximum profit.Example 1: Input:
prices = [1, 2, 3, 0, 2]Output:3Explanation: Buy on day 0, sell on day 1, cooldown on day 2, buy on day 3, sell on day 4.Example 2: Input:
prices = [1]Output:0Constraints:
1 <= prices.length <= 50000 <= prices[i] <= 1000
Prerequisites
Before attempting this problem, you should be comfortable with:
- State Machine DP — tracking multiple mutually exclusive states
- Buy and Sell Stock I/II — simpler versions without cooldown
1. Brute Force / Recursive
Intuition
At each day we are in one of three states: holding a stock, in cooldown (just sold), or idle (no stock, not cooling down). We recurse through each day, trying every valid transition from the current state. This explores all possible buy/sell schedules but is exponential without memoization.
Algorithm
- Define
dfs(i, holding)= max profit from dayionwards, given whether we currently hold a stock. - Base case:
i >= len(prices)→ return0. - If
holding:- Sell today: profit =
prices[i] + dfs(i+2, False)(skip dayi+1for cooldown) - Hold:
dfs(i+1, True) - Return max of these.
- Sell today: profit =
- If not holding:
- Buy today:
-prices[i] + dfs(i+1, True) - Skip:
dfs(i+1, False) - Return max of these.
- Buy today:
Solution
def maxProfit(prices):
def dfs(i, holding):
if i >= len(prices):
return 0
if holding:
sell = prices[i] + dfs(i + 2, False)
hold = dfs(i + 1, True)
return max(sell, hold)
else:
buy = -prices[i] + dfs(i + 1, True)
skip = dfs(i + 1, False)
return max(buy, skip)
return dfs(0, False)
print(maxProfit([1, 2, 3, 0, 2])) # 3
print(maxProfit([1])) # 0
print(maxProfit([2, 1])) # 0
Complexity
- Time:
O(2^n)— exponential without memoization - Space:
O(n)— recursion depth
2. DP with State Machine
Intuition
Model the problem as a state machine with three states at each day:
- holding: currently holding a stock
- sold (cooldown): just sold, must wait one day
- idle: free to buy
Transitions:
holding[i] = max(holding[i-1], idle[i-1] - prices[i])— keep holding, or buy from idlesold[i] = holding[i-1] + prices[i]— sell what we holdidle[i] = max(idle[i-1], sold[i-1])— stay idle, or come out of cooldown
The answer is max(sold[-1], idle[-1]) — we can’t end holding.
Algorithm
- Initialise
holding = -prices[0],sold = 0,idle = 0. - For each day from index
1:new_holding = max(holding, idle - prices[i])new_sold = holding + prices[i]new_idle = max(idle, sold)- Update all three states simultaneously.
- Return
max(sold, idle).
Solution
def maxProfit(prices):
if not prices:
return 0
holding = -prices[0]
sold = 0
idle = 0
for i in range(1, len(prices)):
new_holding = max(holding, idle - prices[i])
new_sold = holding + prices[i]
new_idle = max(idle, sold)
holding, sold, idle = new_holding, new_sold, new_idle
return max(sold, idle)
print(maxProfit([1, 2, 3, 0, 2])) # 3
print(maxProfit([1])) # 0
print(maxProfit([2, 1])) # 0
print(maxProfit([6, 1, 3, 2, 4, 7])) # 6
Complexity
- Time:
O(n) - Space:
O(1)— only three variables needed
Common Pitfalls
Updating states in place without temporaries. If you do holding = max(holding, idle - prices[i]) then immediately use the new holding to compute sold, you’ll get wrong results. Always compute all three new values before assigning.
Skipping two days instead of one. The cooldown is exactly 1 day — you sell on day i and can next buy on day i+2. In the recursive approach, jump to i+2 when selling, not i+3.
Including the holding state in the final answer. You can’t end the simulation while still holding a stock (you’d be sitting on unrealised profit). Only max(sold, idle) makes sense at the end.