Leetcode309. Best Time to Buy and Sell Stock with Cooldown
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
- You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
- After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example:
Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]
动态规划
第一步:定义状态
S0
是没有股票(空仓),S1
是持有股票(持仓),S2
是冷冻期。s[i]
表示到第i
天为止所赚的最大利润(即在第 i
天的操作结束后我目前一共赚到的利润)。
第二步 思考状态转移方程
-
如果第
i
天的状态是空仓(s0
),它可以由第i-1
天的两个状态转换而来:① 第
i-1
天的状态是空仓,我到i-1
天为止赚的利润为s0[i-1]
,在第i
天也不买入,截至第i
天的最大利润为s0[i] = s0[i-1]
② 第
i-1
天的状态是冷冻期,我到i-1
天为止赚的利润为s2[i-1]
,截至第i
天的最大利润为s0[i] = s2[i-1]
③ 由①②我们可以得出空仓状态的状态转移方程:
s0[i] = max(s0[i - 1], s2[i - 1]);
-
如果第
i
天的状态是持仓① 第
i-1
天的状态是空仓,我到i-1
天为止赚的利润为s0[i-1]
,然后在第i
天买入股票花费了price[i]
,这样截至第i
天的最大利润为s1[i] = s0[i-1] - price[i]
② 第
i-1
天的状态是持仓,我到i-1
天为止赚的利润为s1[i-1]
,在第i
天不卖出,截至第i
天的最大利润为s0[i] = s1[i-1]
③ 由①②我们可以得出持仓状态的状态转移方程:
s1[i] = max(s0[i-1] - price[i], s1[i-1])
-
如果第
i
天的状态是冷冻期:那我第i-1
天一定卖出了股票,所以截至第i
天的最大利润一定是s2[i] = s1[i - 1] + price[i]
第三步 思考初始化
结合实际情况,不能理所当然把三个全部初始化为0
第一天商品的价格为 price[0]
,截至第一天,我们获得的最大利润 s[0]
可以是:
① 空仓:第一天什么都不做,s0[0] = 0
② 持仓:第一天买入股票,s1[0] = -price[0]
③ 冷冻期:不可能进入冷冻期,s2[0] = Integer.MIN_VALUE
第四步 思考输出
最后一天我们不可能再买入,即不可能是持仓状态,只可能卖出或者是冷冻期。所以第 i
天的最大利润是
result = max(s0[i],s2[i])
public int maxProfit(int[] prices) {
int len = prices.length;
if (len <= 1) {
return 0;
}
int[] s0 = new int[len];
int[] s1 = new int[len];
int[] s2 = new int[len];
s0[0] = 0;
s1[0] = -prices[0];
s2[0] = Integer.MIN_VALUE;
for (int i = 1; i < len; i++) {
s0[i] = Math.max(s0[i - 1], s2[i - 1]);
s1[i] = Math.max(s1[i - 1], s0[i - 1] - prices[i]);
s2[i] = Math.max(s2[i - 1], s1[i - 1] + prices[i]);
}
return Math.max(s0[len - 1], s2[len - 1]);
}
第五步 思考状态压缩
因为我们只用到了上一次的状态,因此压缩状态
public int maxProfit(int[] prices) {
if (prices == null || prices.length < 2) return 0;
int[] s0 = new int[2];
int[] s1 = new int[2];
int[] s2 = new int[2];
s0[0] = 0;
s1[0] = -prices[0];
s2[0] = Integer.MIN_VALUE;
for (int i = 1; i < prices.length; ++i) {
s0[i%2] = Math.max(s0[(i-1)%2], s2[(i-1)%2]);
s1[i%2] = Math.max(s1[(i-1)%2], s0[(i-1)%2] - prices[i]);
s2[i%2] = s1[(i-1)%2] + prices[i];
}
return Math.max(s0[(prices.length-1)%2], s2[(prices.length-1)%2]);
}