--- title: 'Best Time to Buy and Sell Series' author: Jay Chen tags: LeetCode --- # Best Time to Buy and Sell Series ## Notation - `prices`: stock price array - `k`: max #transactions allowed to complete - `dp[i][k][j]`: the max profix that could be gained - at the end of `i`th day - w/ at most `k` transactions and - w/ `j` stocks in hand AFTER taking the action ## Base case - `dp[-1][k][0] = 0` (`i = -1` means no stock) - `dp[i][0][0] = 0` (no transaction) - `dp[-1][k][1] = -Inf` (no stock but `1` in hand is impossible) - `dp[i][0][1] = -Inf` (no transaction but `1` in hand is impossible) ## Recurrence relations - `dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])`, rest or sell - `dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])`, rest or buy (note that `k - 1` because buying `prices[i]` cost one transaction) ## Case I: `k = 1` [121. Best Time to Buy and Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/) - `dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])` - `dp[i][1][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i])` - `dp[i][1][1] = max(dp[i - 1][1][1], -prices[i])`, where `dp[i - 1][0][0] = 0` (no transaction) ```cpp class Solution { public: int maxProfit(vector<int>& prices) { int sellOne = 0; int holdOne = INT_MIN; for (const int price : prices) { sellOne = max(sellOne, holdOne + price); holdOne = max(holdOne, -price); } return sellOne; } }; ``` ## Case II: `k = +Infinity` [122. Best Time to Buy and Sell Stock II](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/) When $k \to \infty$, there's no difference between `k` and `k - 1`. - `dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])` - `dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])` - `dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k][0] - prices[i])`, where `dp[i - 1][k - 1][0] = T[i - 1][k][0]` ```cpp class Solution { public: int maxProfit(vector<int>& prices) { int sell = 0; int hold = INT_MIN; for (const int price : prices) { sell = max(sell, hold + price); hold = max(hold, sell - price); } return sell; } }; ``` ## Case III: `k = 2` [123. Best Time to Buy and Sell Stock III](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/) - `dp[i][2][0] = max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i])` - `dp[i][2][1] = max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i])` - `dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])` - `dp[i][1][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i])` - `dp[i][1][1] = max(dp[i - 1][1][1], -prices[i])`, where `dp[i - 1][0][0] = 0` (no transaction) ```cpp class Solution { public: int maxProfit(vector<int>& prices) { int sellTwo = 0; int holdTwo = INT_MIN; int sellOne = 0; int holdOne = INT_MIN; for (const int price : prices) { sellTwo = max(sellTwo, holdTwo + price); holdTwo = max(holdTwo, sellOne - price); sellOne = max(sellOne, holdOne + price); holdOne = max(holdOne, -price); } return sellTwo; } }; ``` ## Case IV: `k = arbitrary` [188. Best Time to Buy and Sell Stock IV](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/) - `dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])` - `dp[i][1][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i])` - `dp[i][1][1] = max(dp[i - 1][1][1], -prices[i])`, where `dp[i - 1][0][0] = 0` (no transaction) ```cpp class Solution { public: int maxProfit(int k, vector<int>& prices) { // reduce the problem to `k == +Infinity` if (k >= prices.size() / 2) { int sell = 0; int hold = INT_MIN; for (const int price : prices) { sell = max(sell, hold + price); hold = max(hold, sell - price); } return sell; } vector<int> sell(k + 1); vector<int> hold(k + 1, INT_MIN); for (int price : prices) for (int i = k; i > 0; i--) { sell[i] = max(sell[i], hold[i] + price); hold[i] = max(hold[i], sell[i - 1] - price); } return sell[k]; } }; ``` ## Case V: `k = +Infinity` with cooldown [309. Best Time to Buy and Sell Stock with Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/) - `dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])` - `dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 2][k][0] - prices[i])` ```cpp class Solution { public: int maxProfit(vector<int>& prices) { int sell = 0; int hold = INT_MIN; int prev = 0; for (const int price : prices) { const int cache = sell; sell = max(sell, hold + price); hold = max(hold, prev - price); prev = cache; } return sell; } }; ``` | index | | 0 | 1 | 2 | 3 | 4 | | :----: | :-------: | :-: | :---------: | :---------: | :---------------------------------: | :---------: | | prices | | 1 | 2 | 3 | 0 | 2 | | sell | 0 | 0 | -1 + 2 = 1 | -1 + 3 = 2 | 2 | 1 + 2 = 3 | | hold | $-\infty$ | -1 | -1 | -1 | **1 - 0 = 1** (sell[1] - prices[3]) | 1 | | prev | 0 | 0 | 0 = sell[0] | 1 = sell[1] | 2 = sell[2] | 2 = sell[3] | ## Case VI: `k = +Infinity` with fee [714. Best Time to Buy and Sell Stock with Transaction Fee](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/) **We can charge the fee when buying or selling.** - `dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])` - `dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k][0] - prices[i] - fee)` or - `dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i] - fee)` - `dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k][0] - prices[i])` ```cpp class Solution { public: int maxProfit(vector<int>& prices, int fee) { int sell = 0; int hold = INT_MIN; for (const int price : prices) { sell = max(sell, hold + price); hold = max(hold, sell - price - fee); } return sell; } }; ```