Skip to content

Commit fe33475

Browse files
Add solution and explanation for problem 3573: Best Time to Buy and Sell Stock V
1 parent 69393c9 commit fe33475

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

explanations/3573/en.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
## Explanation
2+
3+
### Strategy (The "Why")
4+
5+
**Restate the problem:** We are given stock prices for each day and allowed to make at most k transactions. Each transaction can be either a normal transaction (buy then sell) or a short selling transaction (sell then buy back). We must find the maximum profit we can earn.
6+
7+
**1.1 Constraints & Complexity:**
8+
9+
- **Input Size:** We have at most 1000 days (prices.length <= 10^3), and each price is between 1 and 10^9. We can make at most k transactions where k <= prices.length / 2.
10+
- **Time Complexity:** O(n * k) where n is the number of days and k is the maximum number of transactions. We iterate through each day and for each day, we process all k transactions.
11+
- **Space Complexity:** O(k) for storing the DP arrays (res, bought, sold) of size k.
12+
- **Edge Case:** If k is 0, we cannot make any transactions, so the profit is 0.
13+
14+
**1.2 High-level approach:**
15+
16+
The goal is to maximize profit by choosing the best sequence of transactions (normal or short selling) up to k transactions. We use dynamic programming to track the best profit at each stage of transaction completion.
17+
18+
**1.3 Brute force vs. optimized strategy:**
19+
20+
- **Brute Force:** Try all possible combinations of transactions (normal or short) at each day, which would be exponential in complexity O(2^(n*k)).
21+
- **Optimized Strategy:** Use dynamic programming with three state arrays: `res[j]` tracks best profit after completing j transactions, `bought[j]` tracks best profit when holding a bought position in the j-th transaction, and `sold[j]` tracks best profit when holding a short position in the j-th transaction. This is O(n * k) time and O(k) space.
22+
- **Optimization:** By maintaining separate states for bought and sold positions, we can efficiently track and update the maximum profit at each stage without recalculating all possibilities.
23+
24+
**1.4 Decomposition:**
25+
26+
1. Initialize three arrays: `res` (completed transactions), `bought` (holding bought positions), and `sold` (holding short positions).
27+
2. For each day's price, process transactions from k down to 1 (to avoid using updated values).
28+
3. For each transaction count j, update `res[j]` by completing either a normal transaction (sell) or short transaction (buy back).
29+
4. Update `bought[j-1]` by starting a new normal transaction (buy at current price).
30+
5. Update `sold[j-1]` by starting a new short transaction (sell at current price).
31+
6. Return the maximum value in `res` array.
32+
33+
### Steps (The "How")
34+
35+
**2.1 Initialization & Example Setup:**
36+
37+
Let's use the example input: `prices = [1, 7, 9, 8, 2]`, `k = 2`.
38+
39+
- Initialize `res = [0, 0, 0]` (profit after 0, 1, or 2 completed transactions)
40+
- Initialize `bought = [-inf, -inf]` (best profit when holding bought position in transaction 0 or 1)
41+
- Initialize `sold = [0, 0]` (best profit when holding short position in transaction 0 or 1)
42+
43+
**2.2 Start Processing:**
44+
45+
We iterate through each price in the array, updating our DP states.
46+
47+
**2.3 Trace Walkthrough:**
48+
49+
| Day | Price | Transaction | Action | res | bought | sold |
50+
|-----|-------|-------------|--------|-----|--------|------|
51+
| 0 | 1 | j=2 | Start normal: bought[1] = max(-inf, res[1] - 1) = max(-inf, 0 - 1) = -1 | [0,0,0] | [-inf,-1] | [0,0] |
52+
| 0 | 1 | j=1 | Start normal: bought[0] = max(-inf, res[0] - 1) = max(-inf, 0 - 1) = -1 | [0,0,0] | [-1,-1] | [0,0] |
53+
| 1 | 7 | j=2 | Complete normal: res[2] = max(0, bought[1] + 7) = max(0, -1 + 7) = 6 | [0,0,6] | [-1,-1] | [0,0] |
54+
| 1 | 7 | j=2 | Start normal: bought[1] = max(-1, res[1] - 7) = max(-1, 0 - 7) = -1 | [0,0,6] | [-1,-1] | [0,0] |
55+
| 1 | 7 | j=1 | Complete normal: res[1] = max(0, bought[0] + 7) = max(0, -1 + 7) = 6 | [0,6,6] | [-1,-1] | [0,0] |
56+
| 1 | 7 | j=1 | Start normal: bought[0] = max(-1, res[0] - 7) = max(-1, 0 - 7) = -1 | [0,6,6] | [-1,-1] | [0,0] |
57+
| 2 | 9 | j=2 | Complete normal: res[2] = max(6, bought[1] + 9) = max(6, -1 + 9) = 8 | [0,6,8] | [-1,-1] | [0,0] |
58+
| 2 | 9 | j=2 | Start normal: bought[1] = max(-1, res[1] - 9) = max(-1, 6 - 9) = -1 | [0,6,8] | [-1,-1] | [0,0] |
59+
| 2 | 9 | j=1 | Complete normal: res[1] = max(6, bought[0] + 9) = max(6, -1 + 9) = 8 | [0,8,8] | [-1,-1] | [0,0] |
60+
| 2 | 9 | j=1 | Start normal: bought[0] = max(-1, res[0] - 9) = max(-1, 0 - 9) = -1 | [0,8,8] | [-1,-1] | [0,0] |
61+
| 3 | 8 | j=2 | Complete normal: res[2] = max(8, bought[1] + 8) = max(8, -1 + 8) = 8 | [0,8,8] | [-1,-1] | [0,0] |
62+
| 3 | 8 | j=2 | Start short: sold[1] = max(0, res[1] + 8) = max(0, 8 + 8) = 16 | [0,8,8] | [-1,-1] | [0,16] |
63+
| 3 | 8 | j=1 | Complete normal: res[1] = max(8, bought[0] + 8) = max(8, -1 + 8) = 8 | [0,8,8] | [-1,-1] | [0,16] |
64+
| 3 | 8 | j=1 | Start short: sold[0] = max(0, res[0] + 8) = max(0, 0 + 8) = 8 | [0,8,8] | [-1,-1] | [8,16] |
65+
| 4 | 2 | j=2 | Complete short: res[2] = max(8, sold[1] - 2) = max(8, 16 - 2) = 14 | [0,8,14] | [-1,-1] | [8,16] |
66+
| 4 | 2 | j=1 | Complete short: res[1] = max(8, sold[0] - 2) = max(8, 8 - 2) = 8 | [0,8,14] | [-1,-1] | [8,16] |
67+
68+
**2.4 Increment and Loop:**
69+
70+
After processing all days, we check all values in the `res` array to find the maximum profit.
71+
72+
**2.5 Return Result:**
73+
74+
The maximum value in `res` is 14, which represents the maximum profit achievable with at most 2 transactions. This comes from: normal transaction (buy at 1, sell at 9) = 8, and short transaction (sell at 8, buy back at 2) = 6, total = 14.
75+

solutions/3573/01.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class Solution:
2+
def maximumProfit(self, prices: List[int], k: int) -> int:
3+
# res[j] - best profit after completing j transactions
4+
res = [0] * (k + 1)
5+
# bought[j] - best profit if holding a "bought" position in j-th transaction
6+
# (bought normally, waiting to sell)
7+
bought = [-float('inf')] * k
8+
# sold[j] - best profit if holding a "short sold" position in j-th transaction
9+
# (sold short, waiting to buy back)
10+
sold = [0] * k
11+
12+
for price in prices:
13+
# Process from k down to 1 to avoid using updated values
14+
for j in range(k, 0, -1):
15+
# Complete a transaction:
16+
# - Complete normal: sell at current price (bought[j-1] + price)
17+
# - Complete short: buy back at current price (sold[j-1] - price)
18+
res[j] = max(res[j], bought[j - 1] + price, sold[j - 1] - price)
19+
20+
# Start a new transaction:
21+
# - Start normal: buy at current price (res[j-1] - price)
22+
bought[j - 1] = max(bought[j - 1], res[j - 1] - price)
23+
# - Start short: sell at current price (res[j-1] + price)
24+
sold[j - 1] = max(sold[j - 1], res[j - 1] + price)
25+
26+
return max(res)
27+

0 commit comments

Comments
 (0)