此前也讲解过买卖股票的最佳时机系列的其他问题,可以阅读贪心算法_Yuan_Source的博客-CSDN博客https://blog.csdn.net/2302_80190174/category_12748026.html
题目描述
给定一个整数数组prices
,其中第 prices[i]
表示第 i
天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例:
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
解题思路
为了解决这个问题,我们可以使用动态规划(Dynamic Programming, DP)的方法,但考虑到卖出股票后有一个冷冻期(即不能立即再次买入),我们需要稍微调整状态的定义。
我们可以定义三个状态数组来跟踪每一天结束时的最大利润:
buy[i]
:表示第i
天结束时,如果持有股票(即最近一次操作是买入),那么可以获得的最大利润。sell[i]
:表示第i
天结束时,如果不持有股票且不在冷冻期(即最近一次操作是卖出),那么可以获得的最大利润。rest[i]
:表示第i
天结束时,如果不持有股票但处于冷冻期(即最近一次操作是卖出,因此不能立即买入),那么可以获得的最大利润。
转移方程:
buy[i]
:要么第i-1
天就持有股票(没有操作),要么第i-1
天不持有股票但今天买入了(不是冷冻期)。buy[i] = max(buy[i-1], rest[i-1] - prices[i])
sell[i]
:要么第i-1
天就不持有股票且不在冷冻期(没有操作),要么第i-1
天持有股票今天卖出了。sell[i] = max(sell[i-1], buy[i-1] + prices[i])
rest[i]
:只能从第i-1
天不持有股票且不在冷冻期的状态转移而来(因为今天是冷冻期)。rest[i] = sell[i-1]
初始化:
buy[0] = -prices[0]
(第一天买入股票)sell[0] = 0
(第一天没有卖出)rest[0] = 0
(第一天不可能是冷冻期)
最终结果:
- 遍历完所有天数后,
max(sell[n-1], rest[n-1])
(其中n
是数组长度)即为所求的最大利润,因为最后一天结束时,我们可能处于非冷冻期的不持有状态或冷冻期的不持有状态。
代码1:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 0) return 0;
vector<int> buy(n, 0);
vector<int> sell(n, 0);
vector<int> rest(n, 0);
buy[0] = -prices[0];
sell[0] = 0;
rest[0] = 0; // 第一天不可能是冷冻期
for (int i = 1; i < n; ++i) {
buy[i] = max(buy[i-1], rest[i-1] - prices[i]);
sell[i] = max(sell[i-1], buy[i-1] + prices[i]);
rest[i] = sell[i-1];
}
// 最后一天结束时,我们可能处于非冷冻期的不持有状态或冷冻期的不持有状态
return max(sell[n-1], rest[n-1]);
}
也可以将三个状态数组合并到二维dp数组。
代码2:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<vector<int>> dp(n,vector<int>(3,0));
dp[0][0] = 0 - prices[0];//买入
dp[0][1] = 0;//卖出
dp[0][2] = 0;//冷冻期
for(int i=1;i<n;i++){
dp[i][0] = max(dp[i-1][2] - prices[i],dp[i-1][0]);
dp[i][1] = max(dp[i-1][0] + prices[i],dp[i-1][1]);
dp[i][2] = dp[i-1][1];
}
return max(dp[n-1][1],dp[n-1][2]);
}
};