1. 题目
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
Related Topics 动态规划
👍 786 👎 0
2. 题解
2.1 解法1: 动态规划
「买入」为负收益,而「卖出」为正收益。在初入股市时,你只有「买入」的权利,只能获得负收益
「处于冷冻期」指的是在第 i 天结束之后的状态。也就是说:如果第 i 天结束之后处于冷冻期,那么第 i+1 天无法买入股票。
- 状态数组: dp[i][0-2],
dp[i][0] 代表第 i 天持有股票的累计最大收益,
dp[i][1] 代表第 i 天不持有股票且在冷冻期内的累计最大收益
dp[i][2] 代表第 i 天不持有股票且不在冷冻期内的累计最大收益 - 递推公式:
- dp[i][0], 可能有两种情况,
a. 前一天就持有股票即 dp[i-1][0]
b. 前一天不持有股票, 且不处于冷冻期中, 此时dp[i][0]=dp[i-1][1]-price[i] - dp[i][1]
a. 说明当天卖出了股票, 前一天持有股票, 有 dp[i][1]=dp[i-1][0]+price[i] - dp[i][2]
a. 前一天不持有股票且不在冷冻期, dp[i][2]
b. 前一天不持有股票且在冷冻期, dp[i][1]
- 初始化: dp[0][0]=-price[i], dp[0][1]=0, dp[0][2]=0
class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 0) {
return 0;
}
int n = prices.length;
int[][] dp = new int[n][3];
dp[0][0] = -prices[0];
for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
dp[i][1] = dp[i - 1][0] + prices[i];
dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]);
}
return Math.max(dp[n - 1][1], dp[n - 1][2]);
}
}
空间优化: 由于递推过程中, dp[i][…] 只使用了前一天的dp[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);
}
}
参考:
官方题解
2.2 写法2
转移方程
每次 sell 之后要等一天才能继续交易。只要把这个特点融入上一题的状态转移方程即可:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])
解释:第 i 天选择 buy 的时候,要从 i-2 的状态转移,而不是 i-1 。
初始化
初始化第 0 天的状态
dp[0][0] = 0
dp[0][1] = -prices[0]
代码:
这个注意需要额外定义一个用于表示 dp[i-2][0]
的变量, 初始时值也为 0 , 具体在循环体内的赋值, 可以先弄清楚各个变量的含义, 然后在草稿纸上演算一下, 是否代表其正确的值, 避免出错
class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 0) {
return 0;
}
// 初始化第 0 天的情况
int dp_i_0 = 0;
int dp_i_1 = -prices[0];
// 代表 dp[i-2][0], 初始时为 0
int dp_pre_0 = 0;
for (int i = 1; i < prices.length; i++) {
int temp = dp_i_0;
dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
dp_i_1 = Math.max(dp_i_1, dp_pre_0 - prices[i]);
dp_pre_0 = temp;
}
return dp_i_0;
}
}
参考:
算法—LeetCode 188. 买卖股票的最佳时机 IV(股票买卖问题总结)
团灭 LeetCode 股票买卖问题—labuladong的算法小抄