Max Profit With K Transactions
Max Profit With K Transactions
Category
Dynamic Programming
Difficulty
Very Hard
Problem Statement
Given an array of positive integers representing the prices of a stock on various days, and an integer k, find the maximum profit that can be obtained by making at most k buy-and-sell transactions. A transaction consists of buying on one day and selling on a later day. You must sell a stock before buying again (you cannot hold more than one share at a time).
Intuition
The key insight is that this problem has optimal substructure: the best profit achievable with k transactions up to day d can be built from the best profit with fewer transactions or fewer days. We define a 2D state where one dimension is the number of transactions used and the other is the day. At each day, we either do nothing or complete a transaction by selling, and the buy point for that transaction was chosen optimally from some earlier day. A running maximum trick eliminates the need for a third inner loop, reducing the time complexity from O(k * n^2) to O(k * n).
Approach
- Create a 2D table
profits[t][d]wheretranges from 0 tokanddranges from 0 ton-1.profits[t][d]represents the max profit using at mostttransactions up to and including dayd. - Base cases:
profits[0][d] = 0for alld(zero transactions means zero profit), andprofits[t][0] = 0for allt(only one day means no transaction can complete). - For each transaction count
tfrom 1 tok, maintain a running maximummaxSoFar = -prices[0]. For each daydfrom 1 ton-1:profits[t][d] = max(profits[t][d-1], prices[d] + maxSoFar)maxSoFar = max(maxSoFar, profits[t-1][d] - prices[d])
- The
maxSoFarvariable tracks the best value ofprofits[t-1][x] - prices[x]for allxup to the current day. This represents the best "effective buy" considering prior profit. - Return
profits[k][n-1].
Pseudocode
function maxProfitWithKTransactions(prices, k):
if prices is empty:
return 0
n = length(prices)
if k > n / 2:
// unlimited transactions — greedy
profit = 0
for d from 1 to n-1:
if prices[d] > prices[d-1]:
profit += prices[d] - prices[d-1]
return profit
profits = 2D array of size (k+1) x n, initialized to 0
for t from 1 to k:
maxSoFar = -prices[0]
for d from 1 to n-1:
profits[t][d] = max(profits[t][d-1], prices[d] + maxSoFar)
maxSoFar = max(maxSoFar, profits[t-1][d] - prices[d])
return profits[k][n-1]
Time & Space Complexity
- Time: O(k * n) where
nis the number of days. We iterate through ak x ntable with constant work per cell thanks to the running maximum optimization. - Space: O(k * n) for the 2D table. This can be reduced to O(n) by noting that row
tonly depends on rowt-1, so only two rows are needed at a time.
Key Insights
- The running maximum variable
maxSoFarabsorbs the inner loop that would otherwise scan all prior days for the optimal buy point, reducing time from O(k * n^2) to O(k * n). - When
k >= n/2, the problem degenerates to "unlimited transactions," which can be solved greedily in O(n) by summing all positive consecutive differences. - Each cell encodes a choice: either skip day
d(carry forwardprofits[t][d-1]) or sell on dayd(take the best prior buy adjusted by earlier profits). - The expression
profits[t-1][x] - prices[x]represents the net position if you had completedt-1transactions optimally and then bought on dayx.
Edge Cases
- Empty prices array: Return 0.
- k = 0: No transactions allowed, return 0.
- Single day: No transaction can complete, return 0.
- k >= n/2: Equivalent to unlimited transactions; switch to greedy approach to avoid unnecessary computation.
- Monotonically decreasing prices: No profitable transaction exists, return 0.
- All prices equal: No profit possible, return 0.