problem:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/
题目的意思已知每天股票的价格,最多进行两次交易(买卖操作),能够赚取的最大利润。(每天最多只能进行一次操作:买或卖;且必须卖出手头持有股票后,才能进行下一次购买)
思路很简单,我们可以把区间划分为两部分,分别求两部分的最大利润值,然后叠加。比较所有划分,求得最大值。
当然,由于我们也可以只进行一次交易,所以还需要考虑不划分时的最大利润值。
基于以上思路,可以很快写出如下O(n^2) 复杂度的算法:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int profit = max(0, getMax(prices, 0, n));
for(int i = 2; i < n - 1;i++)
{
profit = max(profit, getMax(prices, 0, i) + getMax(prices, i, n));
}
return profit;
}
int getMax(vector<int>& prices, int i, int j)
{
int maxnum = INT_MIN;
int minnum = INT_MAX;
for(int k = i; k < j; k++)
{
if(minnum != INT_MAX) maxnum = max(maxnum, prices[k] - minnum);
minnum = min(minnum, prices[k]);
}
return maxnum;
}
};
看起来很清晰,但会超时。原因在于getMax函数存在大量重复计算。比如,在计算左区间( 0, i ) 时,我们可以从( 0, i - 1) 的结果推出, 只需要判断新加入的价格是否高于之前的最高价格,也就是:
dp[ i ] = dp[ i - 1 ] ( price[ i ] <= prevMaxNum ) // dp[ i - 1 ] = prevMaxNum - prevMinNum
dp[ i ] = price[ i ] - prevMinNum ( price[ i ] > prevMaxNum )
优化后,得到O(N)的算法:
class Solution {
public:
int n;
vector<int> leftMax;
vector<int> rightMax;
void calLeftMax(vector<int>& prices)
{
int res = INT_MIN;
int minnum = INT_MAX;
for(int k = 0; k < n; k++)
{
if(minnum != INT_MAX)
{
res = max(res, prices[k] - minnum);
if(res > 0)
{
leftMax[k] = res;
}
}
minnum = min(minnum, prices[k]);
}
}
void calRightMax(vector<int>& prices)
{
int maxnum = INT_MIN;
int res = INT_MIN;
for(int k = n - 1; k >= 0; k--)
{
if(maxnum != INT_MIN)
{
res = max(res, maxnum - prices[k]);
if(res > 0)
{
rightMax[k] = res;
}
}
maxnum = max(maxnum, prices[k]);
}
}
int maxProfit(vector<int>& prices) {
n = prices.size();
if(n == 0) return 0;
leftMax.resize(n, -1);
rightMax.resize(n, -1);
calLeftMax(prices);
calRightMax(prices);
int profit = max(0, leftMax[n - 1]);
for(int i = 2; i < n - 1;i++)
{
profit = max(profit, leftMax[i] + rightMax[i]);
// cout <<leftMax[i] << " " << leftMax[i] << endl;
}
return profit;
}
};
来源:https://www.cnblogs.com/fish1996/p/11261611.html