一道關於股票最大收益的算法題

這原本是一個網友提的問題,我作了很詳細的回答。結果被爬蟲網站爬過去當作文章了。因而整理了一下,發表成文章吧。php

題目是這樣的:算法

給定數組n,包含n天股票的價格price.
一我的一共最多能夠買2手股票,但在第一手股票賣出前不能買入第二手股票。若是不買,收益爲0.假設每手只買1股。計算這我的最大收益。
輸入:[3,8,5,1,7,8]
輸出:12

當時正在Starbucks寫網站,因而就順手寫了以下的代碼:數組

<?php

function getMaxProfilt(array $arr) {
    $len = count($arr);
    $array_tmp = array();
    echo '輔助數組:', '<br />';
    for($i = 0; $i < $len; $i++) {
        for($j = 0; $j < $len; $j++) {
            $array_tmp[$i][$j] = $arr[$j] - $arr[$i];
            echo $array_tmp[$i][$j] . ' ';
        }
        echo '<br />';
    }
    $maxProfit_i = 1;
    $maxProfit_j = 2;
    $maxProfit = $array_tmp[1][2];
    for($i = 1; $i < $len; $i++) {
        for($j = 2; $j < $len; $j++) {
            if($array_tmp[$i][$j] > $maxProfit && $j > $i) {
                $maxProfit = $array_tmp[$i][$j];
                $maxProfit_i = $i;
                $maxProfit_j = $j;
            }
        }
    }
    echo 'maxProfit is :', $maxProfit, '; maxProfit_i is:', $maxProfit_i, '; maxProfit_j is :', $maxProfit_j, '<br />';
    $secondProfit = $array_tmp[0][1];
    $secondProfit_i = 0;
    $secondProfit_j = 1;
    for($i = 0; $i < $maxProfit_i; $i++) {
        //這裏控制第二手買入要在第一手賣出的狀況下才能買入
        for($j = 1; $j < $maxProfit_i; $j++) {
            if($array_tmp[$i][$j] > $secondProfit && $j > $i) {
                $secondProfit = $array_tmp[$i][$j];
                $secondProfit_i = $i;
                $secondProfit_j = $j;
            }
        }
    }
    echo 'second profit is : ', $secondProfit, '; secondProfit_i is :', $secondProfit_i, '; secondProfit_j is :', $secondProfit_j, '<br />';    
    return $maxProfit + $secondProfit;
}

// $array = [3, 8, 5, 1, 7, 8];
// $array = [1,2,3,4,5,6,7,8];
$array = [2,9,1,9,2,4,8,6,2];

echo getMaxProfilt($array);

如下是思路:網站

爲了方便理解,我畫了張圖,以下:spa

輔助圖

程序思路:

定義參數數組爲array;code

一開始的想法

一開始我把問題想的很簡單,覺得只要把兩個最大收益相加就行,由於你有一個條 件,第一手沒有賣出前不能買入第二手。麻煩的就是這裏,因此一開始寫代碼的時候才發現仍是有點複雜。因此用到了二維數組用來控制條件:第二手買入前要賣出第一手;圖片

獲得全部可能且有效的收益:

圖上能看到二維數組的元素都來自於array後面的數減去其前面的數,並且只有右上方纔是真正的收益,假設x軸方向元素下標爲j,y軸方向元素下標爲i.即有效的收益第一條件爲:j>i;rem

解題關鍵

有一個很關鍵的問題要明白,明白這個以後,後面的就好理解了,以下:get

有效收益原則

小明在股價3元的時候買入,在第一個8元的時候賣出,獲得收益5元,這時候,他就永遠不會獲得5元后面的收益,即2,-2,4,5。可是能獲得5的右下角(不包括5所在的行和列)的收益。咱們把這個例子叫作有效收益原則,後面會用到。it

很明顯圖中最大的收益是6和7,可是這違反了有效收益原則。

縮小最大兩個有效收益範圍

根據有效收益原則逆推,若是咱們能肯定最大收益的位置,即7的位置,咱們就能把兩個有效最大收益的範圍縮小,一個是7,另外一個在7(不包括7的行和列)的左上角。因此我在獲得輔助數組後就先找到了7的位置。之因此從-3開始找,是爲了排除第一個5是最大收益的狀況。

最後的條件

獲得了兩個最大收益的範圍,就差最後一個且最重要的條件了:第二手買入必須在第一手賣出以後。
我仍是舉例來講明,根據圖片咱們知道最大收益是7,想要獲得7,第一手就必須在股票價格爲1的時候賣出第一手股票,而後當即買入。或者股票價格爲1的時候第一手股票已經賣出。而7的下標(從0開始)爲i=3,j=5.根據有效收益原則,第二大的收益的範圍就縮小到i=j=3的左上角了。知道了範圍,代碼中第三個雙重循環就能找到第二大的收益了。

代碼講解:

  • 獲得有效收益的二維輔助數組(第一個雙重循環)
  • 獲得最大的有效收益及其位置(第二個雙重循環)
  • 根據上面的位置肯定第二大收益的範圍
  • 根據範圍獲得第二大收益(第三個雙重循環)

總體的過程就是這樣了。不過尚未去分析過複雜度,有興趣的朋友能夠試一試。若是有更好的算法歡迎交流。

相關文章
相關標籤/搜索