DP - Multidimensional · Medium
LeetCode 714: Best Time to Buy and Sell Stock with Transaction Fee — solution
State-machine DP with two values per day, the best profit while holding a share and while holding none. O(n) time and O(1) space.
Problem in plain words
You know a stock's price for each day and may trade as many times as you like, but you can hold at most one share at a time, and every completed buy-and-sell round costs a fixed fee. Find the maximum total profit.
For example, with prices [1, 4, 2, 8] and fee 1, buying at 1 and selling at 8 earns 7 - 1 = 6, while two rounds (1 to 4, then 2 to 8) earn 2 + 5 = 7, which is better.
Intuition
At the end of any day you are in one of two states: holding a share or holding cash only. Track the best profit for each state. Tomorrow's cash is either today's cash (do nothing) or today's holding plus the price minus the fee (sell). Tomorrow's holding is either today's holding (keep it) or today's cash minus the price (buy). This 'small set of states updated per item' style is the same choose-or-skip thinking behind the 0/1 knapsack DP lesson.
Approach
- State:
cashis the best profit with no share in hand;holdis the best profit while holding one share (the purchase price is already subtracted). - Recurrence:
cash = max(cash, hold + price - fee)andhold = max(hold, cash - price). - Base case: before day 0's update,
cash = 0andhold = -prices[0](buying on the first day). - Order: process days 1 to n - 1. Using the freshly updated
cashin theholdupdate is safe: it means selling and re-buying on the same day, which only loses the fee and is never chosen over keeping the share. - Return
cash, since ending with a share in hand is never better.
Dry run
Input: prices = [2, 5, 1, 6], fee = 2. Start: cash = 0, hold = -2.
| Day | Price | cash | hold |
|---|---|---|---|
| 1 | 5 | max(0, -2 + 5 - 2) = 1 | max(-2, 1 - 5) = -2 |
| 2 | 1 | max(1, -2 + 1 - 2) = 1 | max(-2, 1 - 1) = 0 |
| 3 | 6 | max(1, 0 + 6 - 2) = 4 | max(0, 4 - 6) = 0 |
The answer is 4: buy at 2, sell at 5 (profit 1 after fee), buy at 1, sell at 6 (profit 3 after fee).
Java solution
class Solution {
public int maxProfit(int[] prices, int fee) {
int cash = 0; // best profit holding no share
int hold = -prices[0]; // best profit holding one share
for (int i = 1; i < prices.length; i++) {
cash = Math.max(cash, hold + prices[i] - fee); // sell today
hold = Math.max(hold, cash - prices[i]); // buy today
}
return cash;
}
}Python solution
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
cash, hold = 0, -prices[0]
for price in prices[1:]:
cash = max(cash, hold + price - fee) # sell today
hold = max(hold, cash - price) # buy today
return cashComplexity
| Measure | Cost | Why |
|---|---|---|
| Time | O(n) | Two max operations per day. |
| Space | O(1) | Only the two state values are stored. |
Edge cases and common mistakes
- Falling prices: no trade is profitable,
cashstays 0, which is the right answer. - Charging the fee twice: apply it once per round trip, either when buying or when selling, never both.
- Greedy 'sell on every rise': that works for the fee-free version but here many small trades each pay the fee, so it overtrades.
- Single day: the loop does not run and the answer is 0.
›Can I put the fee on the buy instead?
Yes: use hold = max(hold, cash - price - fee) and cash = max(cash, hold + price), starting with hold = -prices[0] - fee. The result is identical.
