123. Best Time to Buy and Sell Stock III***
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/
题目描述
Say you have an array for which the ith
element is the price of a given stock on day i
.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
Example 1:
Input: [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
Example 2:
Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
engaging multiple transactions at the same time. You must sell before buying again.
Example 3:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
解题思路
强烈推荐 Most consistent ways of dealing with the series of stock problems, 文章实在精彩啊, 将有关 Stock 的题总结了一遍, 探索了这些题之间的一致性.
C++ 实现 1
需要注意到的是, 一个隐含的影响 max profit 的因素是我们手中所拥有的股票数量. 因为在每一天我们所能采取的行动只有三种: sell, buy 以及 rest. 然而, 要完成 sell
这个过程, 必须手上拥有一份股票; 而要完成 buy
这个过程, 则必须保证手上没有任何股票. 因此, 倘若设置 T[i][k]
表示在进行最多 k
次交易的情况下在第 i
天所能获得最大收益, 那么它其实可以分成两个部分: T[i][k][0]
以及 T[i][k][1]
, 前者表示采取某种行动之后手上股票变成 0 份所在第 i
天所能获得的最大收益, 而后者表示采取某种行动之后手上股票变成 1 份在第 i
天所能获得的最大收益. 递推关系能写成:
Base cases:
T[-1][k][0] = 0, T[-1][k][1] = -Infinity
T[i][0][0] = 0, T[i][0][1] = -Infinity
Recurrence relations:
T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i]) # sell
T[i][k][1] = max(T[i-1][k][1], T[i-1][k-1][0] - prices[i]) # buy
为了找到最大的收益, 我们只需要遍历 prices
, 并不断更新 T[i][k][0]
以及 T[i][k][1]
, 而最后一天的最大收益就是 T[i][k][0]
, 手上是没有股票才能获得最大收益.
上面是通用的公式, 而题目中要求 K = 2
, 此时, 递推公式变为:
T[i][2][0] = max(T[i-1][2][0], T[i-1][2][1] + prices[i])
T[i][2][1] = max(T[i-1][2][1], T[i-1][1][0] - prices[i])
T[i][1][0] = max(T[i-1][1][0], T[i-1][1][1] + prices[i])
T[i][1][1] = max(T[i-1][1][1], -prices[i])
实现代码如下:
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<int> T1(prices.size() + 1);
T1[0] = 0;
int s0 = 0, s1 = INT32_MIN;
for (int i = 0; i < prices.size(); ++ i) {
s0 = std::max(s0, s1 + prices[i]);
s1 = std::max(s1, -prices[i]);
T1[i + 1] = s0;
}
s0 = 0, s1 = INT32_MIN;
for (int i = 0; i < prices.size(); ++ i) {
s0 = std::max(s0, s1 + prices[i]);
s1 = std::max(s1, T1[i] - prices[i]);
}
return s0;
}
};
作者的实现更贴近公式:
public int maxProfit(int[] prices) {
int T_i10 = 0, T_i11 = Integer.MIN_VALUE;
int T_i20 = 0, T_i21 = Integer.MIN_VALUE;
for (int price : prices) {
T_i20 = Math.max(T_i20, T_i21 + price);
T_i21 = Math.max(T_i21, T_i10 - price);
T_i10 = Math.max(T_i10, T_i11 + price);
T_i11 = Math.max(T_i11, -price);
}
return T_i20;
}
C++ 实现 2
用数组 T
来保存每个 k - 1
的状态. 这个方便扩展到 K 为其他值的情况. 可能需要对 T
进行优化.
class Solution {
public:
int maxProfit(vector<int>& prices) {
int K = 2;
// 实际上, 不论 K 为多大, T 设置为 (2, prices.size() + 1) 的大小就行了
// 但这里为了理解方便, 就设置为 (K + 1, prices.size() + 1) 便于推广到 K != 2 的情况.
vector<vector<int>> T(K + 1, vector<int>(prices.size() + 1, 0));
int s0 = 0, s1 = INT32_MIN;
for (int k = 0; k < K; ++ k) {
s0 = 0, s1 = INT32_MIN;
for (int i = 0; i < prices.size(); ++ i) {
s0 = std::max(s0, s1 + prices[i]); // sell
s1 = std::max(s1, T[k][i] - prices[i]); // buy
T[k + 1][i + 1] = s0;
}
}
return s0;
}
};