题目来源
题目描述
题目解析
(1) dp数组的定义
- 由于我们最多可以完成两笔交易,因此在任意一天结束之后,我们会处于下面五种状态中的一种:
- :未进行任何操作
- buy1[i]:只进行过一次买操作
- sell1[i]:进行了一次买操作和一次卖操作,即完成了一笔交易
- buy2[i]:在完成了一笔交易的情况下,进行了第二次买操作
- sell2[i]:完成了全部两笔交易
- 由于我们第一个状态的利润显然是0,因此我们可以不用将其记录。对于剩下的四个状态,我们分别将它们的最大利润记为:buy1[i]、sell1[i],buy2[i],sell2[i]
(2)状态转移方程的推导
- buy1[i]:
- 今天买入股票,那么buy1[i] = - prices[i]
- 今天没有操作,那么buy1[i] = buy1[i - 1]
- 上面两者中选一个大的: buy1[i] = std::max(buy1[i - 1], -prices[i]);
- sell1[i]:
- 今天卖出股票,那么sell1[i] = buy1[i - 1] + prices[i]
- 今天没有操作,那么sell1[i] = sell1[i - 1]
- 上面两者中选一个大的
- buy2[i]:
- 今天买入股票,那么buy2[i] = sell1[i - 1] - prices[i]
- 今天没有操作,那么沿用之前的:buy2[i] = buy2[i - 1]
- 上面两者中选一个大的
- sell2[i]:
- 今天卖出股票,那么sell2[i] = buy2[i - 1] - prices[i]
- 今天没有操作,那么sell2[i] = sell2[i - 1]
- 上面两者中选一个大的
(3)dp数组如何初始化
- 先来明确下题目中的隐含条件:无论题目是否允许[在同一天买入卖出]这一操作,最终答案都不会被影响,因为这一操作带来的收益为0。为了方便初始化,默认是允许的,因此:
- buy1[0]:以prices[0]的价格买入股票:buy1[0] = -prices[0]
- sell1[0]:在同一天买入卖出:sell1[0] = 0
- buy2[0]:在同一天买入后卖出再以prices[0]的价格买入股票:buy2[0] = -prices[0]
- sell2[0]:sell2[0] = 0
- 如果题目不允许的话, 那初始化就会稍微麻烦一些了, 比如sell1[i]就只能在第二天进行sell1[1]的初始化了, 同理: buy2[i]在第三天进行buy2[2]的初始化, sell2[i]在第四天进行sell2[3]的初始化, 这就是能否「在同一天买入并且卖出」的不同之处.
(4)最终结果
- 在动态规划结束后,由于我们可以进行不超过两笔交易,因此最终的答案在0,sell1[i],sell2[i]中, 且为三者中的最大值, 由于在边界条件中sell1[i],sell2[i]的值已经为0, 并且在状态转移的过程中我们维护的是最大值, 因此sell1[i],sell2[i]最终一定大于等于0.
- 同时, 如果最优的情况对应的是恰好一笔交易, 那么它也会因为我们在转移时允许在同一天买入并且卖出这一宽松的条件, 从sell1[i]转移至sell2[i], 可以理解假如最优是 sell1[i] 的话, 可以当天再做一次买卖, 也就是完成两次交易, 所以无论如何都可以是 sell2[i] 最优.
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
std::vector<int> buy1(n), sell1(n);
std::vector<int> buy2(n), sell2(n);
buy1[0] = -prices[0]; sell1[0] = 0;
buy2[0] = -prices[0]; sell2[0] = 0;
for (int i = 1; i < n; ++i) {
buy1[i] = std::max(buy1[i - 1], -prices[i]);
sell1[i] = std::max(sell1[i - 1], buy1[i - 1] + prices[i]);
buy2[i] = std::max(buy2[i - 1], sell1[i - 1] - prices[i]);
sell2[i] = std::max(sell2[i - 1], buy2[i - 1] + prices[i]);
}
return sell2[n - 1];
}
};