Best Time to Buy and Sell Stock with Cooldown is a classic dynamic-programming problem disguised as a trading question. The trick is to stop thinking about which days to trade and instead track a tiny state machine: each day you are in one of three states, and the answer falls out of how those states feed each other.
Problem. You are given an array prices where prices[i] is the price of a stock on day i. You
may buy and sell as many times as you like, but you must obey two rules: you can hold at most one
share at a time, and after you sell you must rest for one day (the cooldown) before buying
again. Return the maximum profit.
Example: prices = [1, 2, 3, 0, 2] → answer 3 (buy at 1, sell at 3 for +2, cooldown, buy at 0, sell at 2 for +2... but with one share and the cooldown the best achievable here is 3).
The slow way first
The brute-force idea is to try every combination of buy/sell/skip decisions across all days. That branches into an exponential number of choices — far too slow. Even memoizing on (day, do-I-hold-a-share) works but is fiddly because of the cooldown rule.
The question to ask: while standing on one day, what do I actually need to know from yesterday? Only three numbers: the best profit if I currently hold a share, the best if I just sold today, and the best if I am resting. Everything else is noise.
The idea: three states, updated each day
Define three running values, each meaning "the best profit I can have, ending today, in this state":
- hold — I am holding a share today.
- sold — I sold a share today (so tomorrow is a forced cooldown).
- rest — I am idle today and free to buy tomorrow.
Each day they update from yesterday: hold = max(hold, rest - price) (keep holding, or buy from a rested position), sold = hold + price (sell what I held), and rest = max(rest, prev_sold) (stay idle, or roll in yesterday's sale now that the cooldown has passed).
The cooldown is encoded for free: sold never feeds hold directly, only through rest, which lags a day. That one-day gap is the cooldown.
Walk through it
Step through the animation for prices = [1, 2, 3, 0, 2]. The pointer scans each day. Watch the three state boxes: hold stays at -1 while we own a cheap share, sold rises as selling becomes profitable, and rest carries the best banked profit forward. By the last day, max(sold, rest) = 3.
Pseudocode
hold = -infinity # best profit while holding a share
sold = 0 # best profit having just sold today
rest = 0 # best profit while idle (free to buy)
for each price in prices:
prev_sold = sold # capture before sold is overwritten
hold = max(hold, rest - price) # keep holding, or buy from rest
sold = hold + price # sell the share we hold
rest = max(rest, prev_sold) # stay idle, or finish cooldown
return max(sold, rest) # never end while still holdingThe Python solution
def max_profit(prices):
hold = float('-inf')
sold = 0
rest = 0
for price in prices:
prev_sold = sold
hold = max(hold, rest - price)
sold = hold + price
rest = max(rest, prev_sold)
return max(sold, rest)holdstarts at negative infinity because you cannot be holding a share before any day.prev_soldsnapshotssoldbefore the line below overwrites it, sorestuses yesterday's sale — that lag is the cooldown.hold = max(hold, rest - price)is the heart: either keep the share, or buy today, which only the rested state is allowed to do.sold = hold + pricesells the held share at today's price.- The answer is
max(sold, rest), neverhold— you would never finish still owning a share.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (try every decision) | O(2^n) (slow) | exponential branching |
| State machine (this solution) | O(n) (moderate) | one pass, three states |
O(1) (fast)We collapse the whole problem into three numbers that roll forward one day at a time. That gives O(n) time and O(1) space — no array, no recursion stack.
When this pattern shows up
When a problem has a small set of mutually exclusive situations you can be in (holding / sold / resting, or locked / unlocked, etc.) and each day or step transitions between them, model it as a state machine. Track one running value per state and update them all each step.
Ordering matters. rest must use yesterday's sold, so snapshot it into prev_sold before the line
that overwrites sold. Forgetting this lets you skip the cooldown and over-count profit.
Practice
On day 3 (price = 0), rest is 1 from the previous day. What does hold become after hold = max(hold, rest - price)?
1. What do the three states hold, sold, and rest represent?
2. How is the one-day cooldown encoded in the updates?
3. Why capture prev_sold before updating sold?
4. What is the space complexity of this solution?