309. Best Time to Buy and Sell Stock with Cooldown
Description
You are given an array prices where prices[i] is the price of a given stock on the ith day.
Find the maximum profit you can achieve. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times) with the following restrictions:
- After you sell your stock, you cannot buy stock on the next day (i.e., cooldown one day).
Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).
Example 1:
Input: prices = [1,2,3,0,2] Output: 3 Explanation: transactions = [buy, sell, cooldown, buy, sell]
Example 2:
Input: prices = [1] Output: 0
Constraints:
1 <= prices.length <= 50000 <= prices[i] <= 1000
Solutions
Solution 1: Memoization Search
Thinking
We may trade many times, but a sell forces a one-day cooldown. A raw decision tree over day, holding, and cooldown revisits the same states.
Compress to \((i,j)\): starting at day \(i\), whether we hold. Skip the day; if holding, sell and jump to \(i+2\); if free, buy and start holding. Memoization evaluates each state once; the extra day after a sell is the cooldown.
We design a function \(dfs(i, j)\), which represents the maximum profit that can be obtained starting from the \(i\)th day with state \(j\). The values of \(j\) are \(0\) and \(1\), respectively representing currently not holding a stock and holding a stock. The answer is \(dfs(0, 0)\).
The execution logic of the function \(dfs(i, j)\) is as follows:
If \(i \geq n\), it means that there are no more stocks to trade, so return \(0\);
Otherwise, we can choose not to trade, then \(dfs(i, j) = dfs(i + 1, j)\). We can also trade stocks. If \(j > 0\), it means that we currently hold a stock and can sell it, then \(dfs(i, j) = prices[i] + dfs(i + 2, 0)\). If \(j = 0\), it means that we currently do not hold a stock and can buy, then \(dfs(i, j) = -prices[i] + dfs(i + 1, 1)\). Take the maximum value as the return value of the function \(dfs(i, j)\).
The answer is \(dfs(0, 0)\).
To avoid repeated calculations, we use the method of memoization search, and use an array \(f\) to record the return value of \(dfs(i, j)\). If \(f[i][j]\) is not \(-1\), it means that it has been calculated, and we can directly return \(f[i][j]\).
The time complexity is \(O(n)\), and the space complexity is \(O(n)\), where \(n\) is the length of the array \(prices\).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Solution 2: Dynamic Programming
Thinking
The memoized recursion is the same recurrence filled forward. Let \(f[i][0/1]\) be the best profit after day \(i\) free or holding. Free comes from staying free or selling today; holding comes from staying put or buying after cooldown, i.e. from \(f[i-2][0]\).
Fill left to right; the answer is free on the last day. Time stays \(O(n)\) without recursion.
We can also use dynamic programming to solve this problem.
We define \(f[i][j]\) to represent the maximum profit that can be obtained on the \(i\)th day with state \(j\). The values of \(j\) are \(0\) and \(1\), respectively representing currently not holding a stock and holding a stock. Initially, \(f[0][0] = 0\), \(f[0][1] = -prices[0]\).
When \(i \geq 1\), if we currently do not hold a stock, then \(f[i][0]\) can be obtained by transitioning from \(f[i - 1][0]\) and \(f[i - 1][1] + prices[i]\), i.e., \(f[i][0] = \max(f[i - 1][0], f[i - 1][1] + prices[i])\). If we currently hold a stock, then \(f[i][1]\) can be obtained by transitioning from \(f[i - 1][1]\) and \(f[i - 2][0] - prices[i]\), i.e., \(f[i][1] = \max(f[i - 1][1], f[i - 2][0] - prices[i])\). The final answer is \(f[n - 1][0]\).
The time complexity is \(O(n)\), and the space complexity is \(O(n)\), where \(n\) is the length of the array \(prices\).
1 2 3 4 5 6 7 8 9 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 | |
Solution 3: Dynamic Programming (Space Optimization)
Thinking
Method 2 only reads \(i-1\) and \(i-2\), so the full table is unnecessary. Three rolling variables (free two days ago, free yesterday, holding yesterday) implement the same transfers in \(O(1)\) space.
The transition only needs the previous two days, so three variables are enough and the space complexity is \(O(1)\).
1 2 3 4 5 6 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 3 4 5 6 7 | |
1 2 3 4 5 6 7 | |