【算法題】股票買賣問題解法詳解

本解法是股票問題的通用解法,在leetcode上對應以下題:

買賣股票的最佳時機
買賣股票的最佳時機 II
買賣股票的最佳時機 III
買賣股票的最佳時機 IV
買賣股票的最佳時機含手續費
最佳買賣股票時機含冷凍期

下面來說通用解法:
        這類問題有一個狀態轉移圖:

其中:0表示未持有股票,1表示持有股票。則對應於每一天,有持有和未持有兩種情況。
如果某一天持有股票,則可能是前一天就已持有股票或前一天未持有股票,當天買入了股票;如果某一天未持有股票,則可能是前一天就未持有股票或前一天持有股票,當天賣出了股票。
那麼很容易得到如下遞推式:

dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
//            max(   選擇 rest  ,           選擇 sell      )
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
//            max(   選擇 rest  ,           選擇 buy         )

解釋一下:三維dp數組i表示第i天,k表示第k手,0表示當天未持有股票,1表示當天持有股票。
利用以上解法就能輕鬆解出股票買賣問題了,以下分別說明:

  1. k=1
    對應只能買賣一次的情況
dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][1][1] + prices[i])
dp[i][1][1] = max(dp[i-1][1][1], dp[i-1][0][0] - prices[i]) 
            = max(dp[i-1][1][1], -prices[i])

// 由於k均爲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])
  1. k無限制
    對應無限次買賣的情況
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
            = max(dp[i-1][k][1], dp[i-1][k][0] - prices[i])

// 這裏k仍然沒有意義,簡化爲:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
  1. k無限制,有冷卻時間
// 這裏k仍簡化:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])
  1. k無限制,有手續費
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i] - fee)
  1. k不爲1或不限制的情況
    這時k不能簡化,因此轉移方程如下:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])

以k爲指定值爲例(沒有任何簡化的情況),寫出解答代碼:

class Solution {
    public int maxProfit(int k, int[] prices) {
        int[][][] dp = new int[prices.length+1][k+1][2];
        for(int i=0;i<=k;i++){
            dp[0][i][0]=0;
            dp[0][i][1]=Integer.MIN_VALUE;    // 表示不可能
        }
        for(int i=1;i<=prices.length;i++){
            for(int j=1;j<=k;j++){      // 0次交易 值爲0  無需處理
                dp[i][j][0]=Math.max(dp[i-1][j][0], dp[i-1][j][1]+prices[i-1]);
                dp[i][j][1]=Math.max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i-1]);
            }
        }
        return dp[prices.length][k][0];
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章