Leetcode刷题总结:123. 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 (ie, you must sell the stock before you buy again).
最多只能交易两次,且不能重叠
题解:
跟前面计算股票收益1一样,先计算出收益数组,不同的是这次可以交易两次;
考虑用动态规划的方法把数组分成左右两边,分别计算左右两边的最大收益,总收益就是左右两边收益相加;
class Solution { public: int maxSub(vector<int>& prices, int start, int end) { int maxSum = 0; int sum = 0; for (int i = start; i < end; ++i) { sum += prices[i]; sum = max(sum, 0); maxSum = max(sum, maxSum); } return maxSum; } int maxProfit(vector<int>& prices) { vector<int> gain(prices.size(), 0); for (int i = 1; i < prices.size(); ++i) { gain[i] = prices[i] - prices[i-1]; } int maxPro = 0; for (int i = 1; i < gain.size(); ++i) { int sum1 = maxSub(gain, 0, i); int sum2 = maxSub(gain, i, gain.size()); maxPro = max(maxPro, sum1+sum2); } return maxPro; } };
时间复杂度是O(n2), 提交,结果出现case使得运行超时;
考虑如何优化:
显然分析case发现,如果切分点是负数,那么切分完之后,左右两边的最大收益都不会发生改变,所以当gain[i]<0时,跳过该步split然后再计算的过程;
再优化一点,可以记录前一个为正数的的下标prePositiveInd, 切分的时候左边只计算从[0, prePositiveInd],右边计算[i, end];
修改的maxProfit如下:
int maxProfit(vector<int>& prices) { vector<int> gain(prices.size(), 0); for (int i = 1; i < prices.size(); ++i) { gain[i] = prices[i] - prices[i-1]; } int maxPro = 0; int prePositiveInd = 0; for (int i = 1; i < gain.size(); ++i) { if (gain[i] <= 0) { continue; } if (prePositiveInd < i) { prePositiveInd = i; } int sum1 = maxSub(gain, 0, prePositiveInd); prePositiveInd = i; int sum2 = maxSub(gain, i, gain.size()); maxPro = max(maxPro, sum1+sum2); } return maxPro; }
提交,没有超时提示了,但是仍然不理想,只打败了9%, 运行时间为12 ms;
再思考一下有没有可以改进的地方呢?
我们想想其实切分会发生在什么地方呢?
只有前面一次是盈利,而当前这次是亏损,才会在该点上考虑有一次卖出操作;
而买入操作只会在前面是负数而当前是正数的情况下进行一次买入操作;
也就是说 遍历寻找negStart, 以及negEnd, 可以将数组划分为[0, negStart), [negEnd+1, end)两个部分,计算收益;进一步减少了切分和计算maxSub的次数;
int maxProfit(vector<int>& prices) { vector<int> gain(prices.size(), 0); for (int i = 1; i < prices.size(); ++i) { gain[i] = prices[i] - prices[i-1]; } int maxPro = 0; int negStart = 0; for (int i = 1; i < gain.size(); ++i) { if (gain[i] <= 0) { if (negStart == 0) { negStart = i; } continue; } if (negStart > 0) { int sum1 = maxSub(gain, 0, negStart); int sum2 = maxSub(gain, i, gain.size()); maxPro = max(maxPro, sum1+sum2); negStart = 0; } } maxPro = max(maxPro, maxSub(gain, 0, gain.size())); return maxPro; }
提交,计算时间为9ms;
补充:
另外一种思路,
先从前到后计算出前向最大收益preGain,preGain[i]表示从0到i(不包括i)的最大收益
然后计算从后到前的反向最大收益backGain, backGain[i]表示从i到end(包括i)的最大收益;
然后preGain+backGain按下标相加,取最大就是最大总收益