Best Time to Buy and Sell Stock IV is the boss level of the stock-trading family. You may make at most k buy-sell transactions, and you want the maximum total profit. The trick is to carry a tiny pair of running states per transaction and fold each price into them.
Problem. You are given an integer k and an array prices where prices[i] is the stock price on
day i. Complete at most k transactions (each transaction is one buy followed by one later sell;
you cannot hold more than one share at a time) to maximize profit. Return the maximum profit.
Example: k = 2, prices = [3, 2, 6, 5, 0, 3] → answer 7 (buy at 2 sell at 6 = 4, then buy at 0 sell at 3 = 3).
The slow way first
The brute-force idea explores every choice on every day: at each price, for each remaining transaction, decide buy / sell / skip. That branches exponentially. Even a careful recursion over (day, transaction, holding?) is O(n · k) states but costs memory for a full table, and a naive version re-explores overlapping subproblems again and again.
The question to ask: what is the smallest amount of state I must remember to make the next decision? It turns out I only need, for each transaction count t, two numbers: the best balance if I am currently holding a share bought as my t-th buy, and the best profit after completing t sells.
The idea: roll a buy/sell pair per transaction
Keep two arrays of length k + 1:
buy[t]— the best balance (profit minus the price I paid) while holding the share of myt-th buy.sell[t]— the best profit after completing exactlytsells.
For each price, update every transaction level: I can buy my t-th share starting from the profit I had after t − 1 sells, and I can sell my held t-th share to bank profit.
The key insight: buy[t] is funded by sell[t-1], so transactions chain together — the profit from your first sale becomes the budget for your second buy.
Walk through it
Step through the animation. The price pointer scans the prices. Underneath, the buy and sell arrays update for each price. Watch sell[0] jump to 4 when the price hits 6 (buy at 2, sell at 6), and sell[1] reach 7 at the final price 3 (the second transaction: buy at 0, sell at 3). The answer is sell[k] = 7.
Pseudocode
buy[t] = -infinity for every t in 0..k # best balance while holding the t-th share
sell[t] = 0 for every t in 0..k # best profit after t completed sells
for each price in prices:
for t from 1 to k:
buy[t] = max(buy[t], sell[t-1] - price) # buy the t-th share today
sell[t] = max(sell[t], buy[t] + price) # sell the held t-th share today
return sell[k] # best profit using up to k transactionsThe Python solution
def max_profit(k, prices):
buy = [float('-inf')] * (k + 1)
sell = [0] * (k + 1)
for price in prices:
for t in range(1, k + 1):
buy[t] = max(buy[t], sell[t - 1] - price)
sell[t] = max(sell[t], buy[t] + price)
return sell[k]buy[t]starts at-infbecause you have not bought anything yet — no balance is valid.sell[t]starts at0because doing nothing earns zero profit.- For each
price, the inner loop folds it into every transaction leveltfrom1tok. - Line 6: buying your
t-th share spendspriceout of the profit you had aftert − 1sells. - Line 7: selling the share you are holding banks
buy[t] + price. Using the freshly updatedbuy[t]here is fine — it models buying and selling on the same chain of days. sell[k]is the best profit using at mostktransactions (fewer is allowed because eachsell[t]keeps its old value viamax).
Complexity
| Case | Time | Notes |
|---|---|---|
| Time | O(n · k) (moderate) | n prices, k transaction levels each |
| Space | O(k) (moderate) | two arrays of size k + 1 |
O(k) (moderate)We only ever keep 2(k + 1) numbers, no matter how long the price history is. That rolling-state idea — carry the minimal summary per option and fold each new element in — is the heart of these DP problems.
When this pattern shows up
When a problem limits you to at most k of something and asks for an optimal total, think of a DP state
indexed by how many you have used. Stock III is just this with k = 2; Stock II is the unlimited case
(no k dimension at all).
When k is large (≥ len(prices) / 2) you effectively have unlimited transactions, so you can shortcut
to the simple greedy sum of every positive prices[i] − prices[i-1]. Otherwise the O(n · k) table above
is the way.
Practice
With k = 2 and prices = [3, 2, 6, 5, 0, 3], what is sell[1] right after the price 6 is processed?
1. What does buy[t] represent?
2. Why is buy[t] funded by sell[t-1]?
3. What is the time complexity?
4. Why do buy[t] and sell[t] start at -inf and 0?