題目一:已知一個整型數組,表示某隻股票在每天的價格(假定每天價格保持一定,並且可買入可賣出),如果只允許買入一次賣出一次, 問最佳受益是多少?
這題跟數組最大距離恰成互補,這題簡化之後就是 已知A[n], 問找到 j > i, 使得A[j] - A[i] 最大。(數組最大距離是 找到 A[j] > A[i], 使得 j-i 最大)
要求A[j] - A[i] 最大,我們如果以j從0起始遍歷,無非就是讓A[j] 跟左邊元素中的最小值Lmin相比,顯然Lmin可以在j遍歷過程中不斷更新,所以這裏的線性查找就呼之欲出了。
int bestbuysellstock01(const int *A, int n, int& buy, int& sell){
int min=0, maxDiff=0;
buy = sell = 0;
for(int i=1;i<n;++i){
if(A[i] < A[min]){
min = i;
}else{
int diff = A[i] - A[min];
if(diff > maxDiff){
maxDiff = diff;
buy = min;
sell = i;
}
}
}
return maxDiff;
}
很自然,用手指頭想想都能想到題目二:如果不限制交易次數,那麼最佳收益應該是多少?
唔,一次買入賣出是O(n),那麼多次交易貌似是O(n^2), 動態規劃??別急,我們來看看真實情況。
圖一,上證指數日k線圖
很清楚了吧?我們只要把每一段上漲都牢牢抓住,每段收益(比如圖一中圈出的區間)都收入囊中,那就是最大收益!所以,依然是線性,依然是一次遍歷。
int keepbuysellstock(int *A, int n){
int sum=0, buy=0, sell=0;
for(int i=1;i<n;++i){
if(A[i] >= A[i-1]){ //increasing
if(buy == sell){
buy = i-1;
}
sell = i;
}else{ //falling down
if(sell > buy){
sum += A[sell] - A[buy];
sell = buy = i-1;
}
}
}
if(sell > buy)
sum += A[sell] - A[buy];
return sum;
}
代碼實現中,關鍵在於極值點的處理:
1. “底部過後方知是底”,今日價格首次高於昨日價格時,昨日價格纔是局部底;
2. “頂部過後方知是頂”,今日價格首次低於昨日價格時,昨日價格纔是局部頂。
好吧,這道題對於投資過股票的人來說,實在是太親切了:)
注:題目一來自leetcode, 題目二在評論中也有出現,有個人說面試中面試官在出了題目一後緊接着出了題目二,然後他還沒做對。。。所以,不要被題目嚇死啊