Best Time to Buy and Sell Stock III -- LeetCode

iteye_18800發表於2014-04-09
原題連結: http://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/
這道題是 Best Time to Buy and Sell Stock 的擴充套件,現在我們最多可以進行兩次交易。我們仍然使用動態規劃來完成,事實上可以解決非常通用的情況,也就是最多進行k次交易的情況。
這裡我們先解釋最多可以進行k次交易的演算法,然後最多進行兩次我們只需要把k取成2即可。我們還是使用“區域性最優和全域性最優解法”。我們維護兩種量,一個是當前到達第i天可以最多進行j次交易,最好的利潤是多少(global[i][j]),另一個是當前到達第i天,最多可進行j次交易,並且最後一次交易在當天賣出的最好的利潤是多少(local[i][j])。下面我們來看遞推式,全域性的比較簡單,
global[i][j]=max(local[i][j],global[i-1][j]),
也就是去當前區域性最好的,和過往全域性最好的中大的那個(因為最後一次交易如果包含當前天一定在區域性最好的裡面,否則一定在過往全域性最優的裡面)。對於區域性變數的維護,遞推式是
local[i][j]=max(global[i-1][j-1]+max(diff,0),local[i-1][j]+diff),
也就是看兩個量,第一個是全域性到i-1天進行j-1次交易,然後加上今天的交易,如果今天是賺錢的話(也就是前面只要j-1次交易,最後一次交易取當前天),第二個量則是取local第i-1天j次交易,然後加上今天的差值(這裡因為local[i-1][j]比如包含第i-1天賣出的交易,所以現在變成第i天賣出,並不會增加交易次數,而且這裡無論diff是不是大於0都一定要加上,因為否則就不滿足local[i][j]必須在最後一天賣出的條件了)。
上面的演算法中對於天數需要一次掃描,而每次要對交易次數進行遞推式求解,所以時間複雜度是O(n*k),如果是最多進行兩次交易,那麼複雜度還是O(n)。空間上只需要維護當天資料皆可以,所以是O(k),當k=2,則是O(1)。程式碼如下:
public int maxProfit(int[] prices) {
    if(prices==null || prices.length==0)
        return 0;
    int[] local = new int[3];
    int[] global = new int[3];
    for(int i=0;i<prices.length-1;i++)
    {
        int diff = prices[i+1]-prices[i];
        for(int j=2;j>=1;j--)
        {
            local[j] = Math.max(global[j-1]+(diff>0?diff:0), local[j]+diff);
            global[j] = Math.max(local[j],global[j]);
        }
    }
    return global[2];
}
可以看到,這裡的模型是比較複雜的,主要是在遞推式中,local和global是交替求解的。不過理清思路之後,程式碼是非常簡練的,不禁感嘆演算法真是牛逼哈,這麼個複雜生活問題幾行程式碼就解決了。

相關文章