LeetCode 123. 买卖股票的最佳时机 III
📌 题目描述
题目级别:困难
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 示例 1:
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)买入,在第 6 天(股票价格 = 3)卖出,利润 = 3-0 = 3 。随后在第 7 天(股票价格 = 1)买入,在第 8 天 (股票价格 = 4)卖出,利润 = 4-1 = 3 。 - 示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天买入,第 5 天卖出,利润 = 4。 - 示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这个情况下,没有交易完成,所以最大利润为 0。 - 提示:
1 <= prices.length <= 10^50 <= prices[i] <= 10^5
💡 破题思路:状态机 DP (Finite State Machine DP)
这道题是整个股票交易问题家族的"分水岭"。通过维护交易轮次 kkk 与持仓状态(持有 / 未持有),可以建立状态机转移。
1. 状态定义
设在第 iii 天结束时:
buy[j]:当前完成了 jjj 次买入动作、且手头持有股票时的最大资金收益。sell[j]:当前完成了 jjj 次卖出动作、且手头空仓时的最大资金收益。
2. 状态转移方程
对于每天的股价 price,第 jjj 次交易的状态更新如下:
- 进入持仓状态
buy[j]:
可以选择继续保持昨天的持仓buy[j],也可以在上一次卖出空仓sell[j - 1]的基础上按今天价格买入:
buyj=max(buyj,sellj−1−price)buyj = \max(buyj, sellj - 1 - price)buyj=max(buyj,sellj−1−price) - 进入空仓状态
sell[j]:
可以选择继续保持昨天的空仓sell[j],也可以在当前持仓buy[j]的基础上按今天价格卖出:
sellj=max(sellj,buyj+price)sellj = \max(sellj, buyj + price)sellj=max(sellj,buyj+price)
3. 初始化细节
buy数组初始化为负无穷大(如INT_MIN或-prices[0]),表示初始时在未产生现金流前无法凭空持有股票。sell数组初始化为0,表示还没开始交易时本金利润为 0。- 这种向量化的写法天生具备高度通用性 ,只需将 k=2k = 2k=2 改为任意正整数,即可直接无缝 AC 《LeetCode 188. 买卖股票的最佳时机 IV》(最多完成 kkk 笔交易)。
💻 C++ 代码实现 (原汁原味作者版)
cpp
class Solution {
public:
int maxProfit(vector<int>& prices) {
int k = 2;
// buy[i] 表示完成第 i 次买入且持股时的最大利润
// sell[i] 表示完成第 i 次卖出且空仓时的最大利润
vector<int> buy(k + 1, INT_MIN), sell(k + 1, 0);
for (auto price : prices)
{
for (int i = 1; i <= k; i ++ )
{
buy[i] = max(buy[i], sell[i - 1] - price);
sell[i] = max(sell[i], buy[i] + price);
}
}
return sell[k];
}
};