一、题目
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
二、 解决
股票交易系列:
- 121 Best Time to Buy and Sell Stock
- 122 Best Time to Buy and Sell Stock II
- 123 Best Time to Buy and Sell Stock III
- 188 Best Time to Buy and Sell Stock IV
- 309 Best Time to Buy and Sell Stock with Cooldown
- 714 Best Time to Buy and Sell Stock with Transaction Fee
1、动态规划
版本1
思路:
不要关注冷冻期,只关注卖出的那一天。
1、状态定义
f[i]:第 i 天结束之后的累计最大收益。
由于最多只能买入一支股票,并且卖出后有冷冻期的限制,因此有三种不同状态。
f[i][0]: 手上持有股票的最大收益
f[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
f[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益
2、状态转移
f[i][0] = max(f[i−1][0], f[i−1][2]−prices[i])
f[i][1] = f[i−1][0] + prices[i]
f[i][2] = max(f[i−1][1], f[i−1][2])
res:max(f[n−1][0],f[n−1][1],f[n−1][2])
代码:
class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 0) {
return 0;
}
int n = prices.length;
// f[i][0]: 手上持有股票的最大收益
// f[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
// f[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益
int[][] f = new int[n][3];
f[0][0] = -prices[0];
for (int i = 1; i < n; ++i) {
f[i][0] = Math.max(f[i - 1][0], f[i - 1][2] - prices[i]);
f[i][1] = f[i - 1][0] + prices[i];
f[i][2] = Math.max(f[i - 1][1], f[i - 1][2]);
}
return Math.max(f[n - 1][1], f[n - 1][2]);
}
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
版本2
思路:
上面f[i][m]
只与f[i-1][n]
有关系,因为只需要将f[i-1][0], f[i-1][1], f[i-2][2]
存放在三个变量中,通过它们计算出f[i][0],f[i][1], f[1][2]
并存回对应变量,以便第i+1
天的状态转移即可。
代码:
class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 0) {
return 0;
}
int n = prices.length;
int f0 = -prices[0];
int f1 = 0;
int f2 = 0;
for (int i = 1; i < n; ++i) {
int newf0 = Math.max(f0, f2 - prices[i]);
int newf1 = f0 + prices[i];
int newf2 = Math.max(f1, f2);
f0 = newf0;
f1 = newf1;
f2 = newf2;
}
return Math.max(f1, f2);
}
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
版本3
思路:
与版本2类似,只是这里可读性更强。
代码:
class Solution {
public int maxProfit(int[] prices) {
int sell = 0, prev_sell = 0, buy = Integer.MIN_VALUE, prev_buy;
for (int price : prices) {
prev_buy = buy;
buy = Math.max(prev_sell - price, prev_buy);
prev_sell = sell;
sell = Math.max(prev_buy + price, prev_sell);
}
return sell;
}
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
三、参考
1、最佳买卖股票时机含冷冻期
2、非状态机的DP讲解,全新思路,超通俗易懂,包你一遍看懂
3、Share my thinking process
4、Most consistent ways of dealing with the series of stock problems
5、Easiest JAVA solution with explanations