標籤:des style blog http io ar color os 使用
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).
解法一:基於Best Time to Buy and Sell Stock
代碼比較長,但是思路還是比較清晰,聽我解釋:
核心思想:
第一步:依照Best Time to Buy and Sell Stock求得最優買賣。值為maxGap,買賣點記作minInd和maxInd。
第二步:分兩種情況
case1: 在最優買賣的基礎上,另外加入一次有效買賣。
case2: 拆分最優買賣,即minInd在第一次買賣中,maxInd在第二次買賣中。
看著比較簡單,接下來我們糾結邊界情況:
case1中: 新加的一次有效買賣與最優買賣沒有交集。也就是說在minInd嚴格之前或者maxInd嚴格之後。
case2中: 第一次買賣的買點和賣點均有可能為minInd,第二次買賣的賣點和賣點均有可能為maxInd。
實現:
case1很容易,就是再跑兩次最優買賣,擇優加入。若不存在有效買賣則記為0。
minInd前的最優買賣記為partGap1,maxInd後的最優買賣記為partGap2。
case1Result = maxGap + max(partGap1, partGap2);
case2比較麻煩,我們來詳細解釋一下:
簡單分析可知:
兩次買賣的值分別為<minLeft, prices[j]>,<prices[k], maxRight>
其中minLeft為包括minInd在內的,minInd之前的最小值。minLeft為第一次買點的值,prices[j]為第一次賣點的值。
maxRight為包括maxInd在內的,maxInd之後的最大值。prices[k]為第二次買點的值,maxRight為第二次賣點的值。
因此必須滿足以下條件:
(1)minLeft所在位置<=minInd, j>=minInd
(2)k<=maxInd, maxRight所在位置>=maxInd
(3)j<=k 滿足Note的條件
case2Result = prices[j]-minLeft+maxRight-prices[k]
因為minLeft和maxRight都是確定的,最大化case2Result只需要求prices[j]-prices[k]的最大值。
這個問題化歸成了"最差買賣",即最優買賣問題的逆反問題,高點買入,低點賣出。
為了節省計算量,我在實現過程中,minLeft和maxRight都在case1的計算過程中附帶求出來了。
需要注意的是case1是不包含邊界的,因此minLeft和maxRight還應該與minInd、maxInd值權衡一下。
class Solution {public: int maxProfit(vector<int> &prices) { if(prices.empty()) return 0; //at least 1 element int maxV = prices[0]; int maxInd = 0; int minV = prices[0]; int minInd = 0; int curGap = 0; int maxGap = 0; for(vector<int>::size_type st = 1; st < prices.size(); st ++) { if(prices[st] > maxV) {//maxV is free to update maxV = prices[st]; curGap = maxV-minV; if(curGap > maxGap) { maxGap = curGap; maxInd = st; //minInd is the landmark position, changing all the time, thus cannot be determined } } else if(prices[st] < minV) {//if minV changes, all should restart minV = prices[st]; maxV = prices[st]; curGap = 0; } } //get the minInd for(vector<int>::size_type st = 0; st < prices.size(); st ++) { if(prices[maxInd]-prices[st] == maxGap) { minInd = st; break; } } //arrive here, maxGap, maxInd, minInd is needed, thus avoid changing value //case 1: the minInd~max is covered and add the 0~minInd(partGap1) or max~size-1(partGap2) maxV = prices[0]; minV = prices[0]; curGap = 0; int partGap1 = 0; //minLeft can be prices[minInd] int minLeft = prices[minInd]; for(vector<int>::size_type st = 1; st < minInd; st ++) { minLeft = min(minLeft, prices[st]); if(prices[st] > maxV) {//maxV is free to update maxV = prices[st]; curGap = maxV-minV; if(curGap > partGap1) partGap1 = curGap; } else if(prices[st] < minV) {//if minV changes, all should restart minV = prices[st]; maxV = prices[st]; curGap = 0; } } maxV = prices[maxInd+1]; minV = prices[maxInd+1]; curGap = 0; int partGap2 = 0; //if maxInd are greater than prices.size()-3, then partGap2 remains 0. int maxRight = prices[maxInd]; //maxRight initialized to prices[maxInd] for(vector<int>::size_type st = maxInd+1; st < prices.size(); st ++) { maxRight = max(maxRight, prices[st]); if(prices[st] > maxV) {//maxV is free to update maxV = prices[st]; curGap = maxV-minV; if(curGap > partGap2) partGap2 = curGap; } else if(prices[st] < minV) {//if minV changes, all should restart minV = prices[st]; maxV = prices[st]; curGap = 0; } } //arrive here, partGap2, maxRight is needed //caseResult may be either maxGap itself or adding one part int case1Result = maxGap+max(partGap1, partGap2); //case 2: i(index of minLeft<minInd)~minInd~j add k~maxInd~l(index of maxRight>k) //case2Result = prices[j]-minLeft+maxRight-prices[k], that is max(prices[j]-prices[k]) && j<=k maxV = prices[minInd]; //j minV = prices[minInd]; //k curGap = 0; maxGap = 0; //now maxGap can be changed int tmpInd = minInd; for(vector<int>::size_type st = minInd; st <= maxInd; st ++) { if(prices[st] < minV) { minV = prices[st]; curGap = maxV-minV; if(curGap > maxGap) { maxGap = curGap; tmpInd = st; //maxInd is the landmark position, changing all the time, thus cannot be determined } } else if(prices[st] > maxV) { maxV = prices[st]; minV = prices[st]; curGap = 0; } } int case2Result = maxGap-minLeft+maxRight; return max(case1Result, case2Result); }};
解法二:動態規劃
使用兩個數組,maxVec和minVec。
maxVec[i]表示從0到i的最優買賣值(低買高賣,為正)。需要一次從左往右遍曆。
minVec[i]表示從size-1到i的最差買賣值(高買低賣,為負)。需要一次從右往左遍曆。
一次遍曆數組,將對應元素相減:maxVec[i]-minVec[i]的最大值即解。
稍微分析一下發現,其實minVec是不需要的,在從右往左時候的當前值minGap即為minVec[i],
直接計算maxVec[i]-minGap,用來更新結果。這樣還能免去最後在數組上面的一次遍曆。
class Solution {public: int maxProfit(vector<int> &prices) { if(prices.empty()) return 0; int size = prices.size(); int minLeft = prices[0]; int maxGap = 0; //maxVec[i] means the maxGap from start to i (-->) vector<int> maxVec(size, 0); int maxRight = prices[size-1]; int minGap = 0; //minVec[i] means the minGap from end to i (<--) //vector<int> minVec(size, 0); int result = 0; for(int i = 0; i < size; i ++) { if(prices[i]-minLeft > maxGap) //current is very big maxGap = prices[i]-minLeft; else if(prices[i] < minLeft) //current is very small minLeft = prices[i]; maxVec[i] = maxGap; } for(int i = size-1; i >= 0; i --) { if(prices[i]-maxRight < minGap) //current is very small minGap = prices[i]-maxRight; else if(prices[i] > maxRight) //current is very big maxRight = prices[i]; //minVec[i] = minGap; if(maxVec[i]-minGap>result) result = maxVec[i]-minGap; } return result; }};
【LeetCode】Best Time to Buy and Sell Stock III (2 solutions)