買賣股票的最佳時機--動態規划


給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多只允許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。

注意你不能在買入股票前賣出股票。

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
     注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格。

示例2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0

 

解法一: 暴力解決法

算法思想:

我們需要找出給定數組中兩個數字之間的最大差值(即,最大利潤)。此外,第二個數字(賣出價格)必須大於第一個數字(買入價格)。

對於每組 i 和 j (j > i)我們需要找出max(prices[j] - prices[i])

代碼如下:

func maxProfit(_ prices: [Int]) -> Int {
    guard prices.count > 0 else {return 0}
    var maxProfit: Int = 0
    for i in 0..<prices.count - 1 {
        for j in i+1..<prices.count {
            maxProfit = max(maxProfit, prices[j] - prices[i])
        }
    }
    return maxProfit
}

 

解法二:動態規划

算法思想:

動態規划的3個步驟:

  1. 設定狀態
  2. 推導方程
  3. 起始值和輸出

這里狀態的設置得稍微想一想,數組的問題一般我們得固定其中一個變量。這道題有兩個變量,一個是“買入”,另一個是“賣出”,這里我們固定“賣出”變量,“買入”變量一定在它之前。

因此狀態dp[i]表示:在索引為 i 的這一天,用戶所能獲得的最大利潤。

下面思考狀態轉移,我們就要想dp [i] 的從前面的狀態值轉移過來,但是我們就會發現新的問題dp[i - 1]、dp[i - 2]、……、dp[0],狀態轉移無從下手。這里就說明:狀態還不夠,得把“買入”和“賣出”操作加入到狀態的設置中。

於是下面修改狀態的定義:

  • dp[i][0]表示:在索引為 i 的這一天,用戶手上不持股所能獲得的最大利潤;
10 一般表示沒有,在索引為 i 的這一天,手上沒有股票,語義上也是清晰的,下面對狀態 1 的理解也是一樣;
2、   “用戶手上不持股”不代表用戶一定在索引為 i 的這一天把股票拋售了;
3、    在索引為 i 的這一天具體應該這樣理解:從索引為 0 的天數開始,到索引為 i 的這一天,因此,這個狀態的設置具有“前綴”的意味,因此輸出是 dp[len - 1][0],不可能是 dp[len - 1][1],在只發生一次交易的情況下,持有這支股票一定不能使我們獲得最大利潤。
  • 狀態dp[i][1]表示:在索引為 i 的這一天,用戶手上持股所能獲得最大的利潤。

下面考慮狀態轉移方程:

  1. dp[i][0] 可以由誰轉移過來?
  • dp[i - 1][0] :今天不持股,假設我今天什么都不操作,當然可以從昨天不持股轉移過來,這一點是顯然的
  • dp[i - 1][1] + prices[i]:昨天持股,就在索引為 i 的這一天,我賣出了股票,狀態由 1 變成了 0,此時獲得利潤,因此加上這一天的股價。

    綜上:dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);

  2. dp[i][1] 可以由誰轉移過來?

 

  • dp[i - 1][1] :今天持股,假設我今天什么都不操作,當然可以從昨天持股轉移過來,這一點是顯然的;

  • -prices[i]:**請注意:**狀態 1 不能由狀態 0 來,因為事實上,狀態 0 特指:“賣出股票以后不持有股票的狀態”,請注意這個狀態和“沒有進行過任何一次交易的不持有股票的狀態”的區別。

    因此,-prices[i] 就表示,在索引為 i 的這一天,執行買入操作得到的收益,再次強調:因為題目只允許一次交易,因此不能加上 dp[i - 1][0]

綜上:dp[i][1] = max(dp[i - 1][1], -prices[i]);

在新修改的定義中,我們已經分析出了輸出dp[len - 1][0]

 

public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len < 2) {
            return 0;
        }

        // 0:用戶手上不持股所能獲得的最大利潤,特指賣出股票以后的不持股,非指沒有進行過任何交易的不持股
        // 1:用戶手上持股所能獲得的最大利潤

        // 注意:因為題目限制只能交易一次,因此狀態只可能從 1 到 0,不可能從 0 到 1
        // 狀態轉移方程:
        // dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
        // dp[i][1] = max(dp[i - 1][1], -prices[i])
        int[][] dp = new int[len][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < len; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
        }
        return dp[len - 1][0];
    }

 

以上就是股票最大收益算法,希望對大家有所幫助!!! 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM