Last Updated: March 28, 2026
This is a twist on the classic stock trading series. You can make unlimited transactions, but there's a catch: after every sell, you must wait one day before buying again. That cooldown changes the entire decision structure.
Without the cooldown, you could greedily grab every upward price movement. With it, selling on day i locks you out of buying on day i+1. So a sell today might cost you a better opportunity tomorrow. The question becomes: at each point, are you better off selling now and sitting out a day, or holding and selling later?
This naturally leads to a state machine. On any given day, you're in one of a few states, and each state has different transitions available. The cooldown introduces a third state beyond the usual "holding" and "not holding" that makes this problem more interesting than the basic stock variants.
1 <= prices.length <= 5000 → With n up to 5000, O(n^2) is 25 million operations which is borderline. O(n) would be comfortable, and O(n log n) is fine too.0 <= prices[i] <= 1000 → Prices can be zero, which means buying for free is possible. Prices are bounded, so overflow isn't a concern.The most natural way to think about this problem is top-down. At each day, you make a choice based on your current state. If you're not holding stock and not in cooldown, you can buy or skip. If you're holding stock, you can sell or hold. If you just sold, you're forced to cool down.
Let's define a recursive function dfs(day, holding) that returns the maximum profit from day day onwards, where holding indicates whether we currently hold a stock. After a sell, we skip the next day (jumping to day + 2), which naturally enforces the cooldown.
The recursive structure gives us overlapping subproblems. For example, dfs(3, false) might be called from multiple paths: from selling on day 1 (cooldown day 2, then day 3) or from skipping days 1 and 2. Memoization eliminates the redundant computation.
dfs(day, holding) as the max profit from day onwards.day >= n, return 0.prices[day] + dfs(day + 2, false), because selling triggers cooldown) or hold (dfs(day + 1, true)).-prices[day] + dfs(day + 1, true)) or skip (dfs(day + 1, false)).(day, holding).dfs(0, false).The recursion explores all paths. Starting from dfs(0, false):
(day, holding) pair is computed once and cached, giving us O(n) total work.The recursion with memoization works, but it carries overhead from the call stack and memo lookups. Since we process days sequentially, can we fill a table iteratively and avoid the stack entirely?
Let's think about this as a state machine. On any given day, you're in one of three states:
The transitions are: from Rest, buy (to Held) or do nothing (stay Rest). From Held, sell (to Sold) or do nothing (stay Held). From Sold, you must cool down (to Rest). This gives us three recurrences that we compute for each day.
The three states capture every possible situation you can be in on any day. The key insight is that the cooldown forces a mandatory one-day gap between selling and buying. By splitting "not holding" into two separate states (Sold and Rest), the cooldown rule enforces itself naturally through the state transitions. You can only buy from Rest, and Sold transitions to Rest after one day. There's no need for any special cooldown tracking.
held[0] = -prices[0], sold[0] = 0, rest[0] = 0.held[i] = max(held[i-1], rest[i-1] - prices[i])sold[i] = held[i-1] + prices[i]rest[i] = max(rest[i-1], sold[i-1])max(sold[n-1], rest[n-1]).The time is already O(n), but we're storing three entire arrays when the recurrence only looks at the previous day. Can we just track three variables and overwrite them each day?
Since each day's computation only uses the previous day's values, we can replace the three arrays with three variables. This is the same optimization we'd apply to Fibonacci or House Robber: when the DP recurrence has a window of size 1, collapse the array to variables.
The only subtlety is update order. We need to use the old values of all three states when computing the new values. The cleanest fix is to save all three old values in temporaries before updating any of them.
held = -prices[0], sold = 0, rest = 0.prevHeld = held, prevSold = sold, prevRest = rest.held = max(prevHeld, prevRest - prices[i])sold = prevHeld + prices[i]rest = max(prevRest, prevSold)max(sold, rest).