股票交易問題全拿下,一掃光

上來直接最難的,後面都是毛毛雨

問題一:買賣股票的最佳時機IV(leetcode188)

問題二:最大可交易次數不限(leetcode122)

問題三:最大交易次數爲1(leetcode121)

問題四:最大交易次數爲2(leetcode123)

問題五:買賣股票最佳時間含手續費(leetcode714)

問題六:最佳股票買賣時機含冷凍期(leetcode309)


問題一:買賣股票的最佳時機IV(leetcode188)

問題描述:

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

設計一個算法來計算你所能獲取的最大利潤。你最多可以完成 k 筆交易。

注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

大體思路:

二維數組dp0[i][j]表示在第i天,最大可交易次數爲j時不持有股票的最大收益;

同理dp1[i][j]表示在第天,最大可交易次數爲j時持有股票的最大收益。i從0到最大天數,j從0到k。

dp0[i][j] = max(dp0[i - 1][j], dp1[i - 1][j] + prices[i])

dp1[i][j] = max(dp1[i - 1][j], dp0[i - 1][j - 1] - prices[i])

第一個式子:當天沒有股票有兩種可能,要麼前一天就沒持有股票;要麼爲前一天持有股票,當天賣了。

第二個式子:當天持有股票有兩種可能,要麼前一天就持有股票,或者前一天沒持有股票,當天買入了。

注:這裏需要注意的是第二個式子中,第二種情況下的前一天的最大可交易次數爲j-1而不爲j,這是由於買入的次數要小於最大買入次數,假設爲dp0[i - 1][j] 他可能在i-1天之前已經進行過j次交易了,在第i天坑定不能再買入了。

傳遞函數有了,下面描述其的初值。

我們從傳遞函數發現,當前值總是與其正上方的值和左上角的值有關,因此只要對dp0和dp1的第一列第一行初始化即可。

dp0的第一列和第一行均爲0,首先第一列j=0,由於最大可交易次數爲0,那坑定不能買了爲0,第一行i= 0,由於在第1天,不持有股票只有一種可能就是沒有買所以爲0。

dp1的第一列,j= 0最大可交易次數爲0,但是還要持有,這是不可能實現的,因此爲非法狀態統統設爲負無窮;第一行(除第一個元素)i = 0,可交易次數大於0了,在第1天想持有股票,只能買第一天的,因此統統設爲 -1 * prices[0]。

實現代碼如下:

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(k == 0 || prices.length <= 1){
            return 0;
        }
        if(k > prices.length / 2){
            return maxProfit(prices);
        }
        int[][] dp0 = new int[prices.length][k + 1];
        int[][] dp1 = new int[prices.length][k + 1];
        /* dp0[i][0] 和 dp0[0][j] 均爲0*/
        for(int i = 0; i < prices.length; i++){
            dp1[i][0] = Integer.MIN_VALUE;
        }
        for(int j = 1; j < k + 1; j++){
            dp1[0][j] = -1 * prices[0];
        }
        for(int i = 1; i < prices.length; i++){
            for(int j = 1; j < k + 1; j++){
                dp0[i][j] = Math.max(dp0[i - 1][j], dp1[i - 1][j] + prices[i]);
                dp1[i][j] = Math.max(dp1[i - 1][j], dp0[i - 1][j - 1] - prices[i]);
            }
        }
        return dp0[prices.length - 1][k];
    }
    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];
        int[] dp1 = new int[prices.length];
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < dp0.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 1] - prices[i]);
        }
        return dp0[dp0.length - 1];
    }
}

由於k可能很大,當k > n / 2時問題就會轉化爲不用考慮最大交易次數的情況,可以認爲最大交易次數爲無窮。後面的代碼下一節將會分析。

問題二:最大可交易次數不限(leetcode122)

該問題是問題一中k = 無窮的特例,此時 k = k - 1,因此轉移函數變爲

dp0[i] = max(dp0[i - 1], dp1[i - 1] + prices[i])

dp1[i] = max(dp1[i - 1], dp0[i - 1] - prices[i])

問題由二維dp轉化爲一維dp

初值爲:d0[0] = 0,沒買肯定是0啊, d1[0] = -prices[0],買了肯定是- prices[0]。

實現代碼如下:

    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];
        int[] dp1 = new int[prices.length];
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < dp0.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 1] - prices[i]);
        }
        return dp0[dp0.length - 1];
    }

 問題三:最大交易次數爲1(leetcode121)

dp0[i][j] = max(dp0[i - 1][j], dp1[i - 1][j] + prices[i])

dp1[i][j] = max(dp1[i - 1][j], dp0[i - 1][j - 1] - prices[i])

將上式中的j換成1,得到如下:

dp0[i][1] = max(dp0[i - 1][1], dp1[i - 1][1] + prices[i])

dp1[i][1] = max(dp1[i - 1][1], dp0[i - 1][0] - prices[i])

由於dp0[i - 1][0] = 0,所以最終化簡後的轉移方程爲:

dp0[i]= max(dp0[i - 1], dp1[i - 1]+ prices[i])

dp1[i]= max(dp1[i - 1], 0 - prices[i])

初值:dp0[0] = 0, dp1[0] = -prices[0]

該結果也可以使用自然語言理解,當天不持有的有兩種情況,或者前一天就不持有,或者前一天持有當天賣出去了;當天持有的兩種情況爲:前一天就持有,或者前一天不會持有,當天買了。由於只能買一次,前一天不持有說明之前也沒進行過交易,因此前一天不持有的最大收益爲0.

實現代碼如下:

    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];//  dp0[i] i天不持有股票的最大利潤
        int[] dp1 = new int[prices.length]; // 持有股票的最大利潤 
        dp0[0] = 0;
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < prices.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], 0 - prices[i]);
        }
        return dp0[dp0.length - 1];
    }

當然由於只交易一次,因此還可以很多其他辦法解決,比如可以使用貪心的策略,找到當前點之前最小的元素,即在前面那個位置買,在當前點賣。由於本文主要將dp,這裏就不多加贅述了。

問題四:最大交易次數爲2(leetcode123)

這問題沒啥說的,就令k=2咯,直接搞。

代碼如下:

    public int maxProfit(int[] prices) {
        if(prices.length <= 1){
            return 0;
        }
        int K = 2;
        int[][] dp0 = new int[prices.length][K + 1];
        int[][] dp1 = new int[prices.length][K + 1]; 
        // dp0[0][k] 和 dp0[i][0] 均爲0
        for(int i = 0; i < prices.length; i++){ // 第一列 最大允許交易次數爲0 還要持有 定爲非法
             dp1[i][0] = Integer.MIN_VALUE;
        }
        for(int k = 1; k <= K; k++){ //第一行
            dp1[0][k] = -1 * prices[0];
        }
        for(int i = 1; i < prices.length; i++){
            for(int k = 1; k <= K; k++){
                dp0[i][k] = Math.max(dp0[i - 1][k], dp1[i - 1][k] + prices[i]);
                dp1[i][k] = Math.max(dp1[i - 1][k], dp0[i - 1][k - 1] - prices[i]);
            }
        }
        return dp0[prices.length - 1][K];

    }

問題五:買賣股票最佳時間含手續費(leetcode714)

問題描述:

給定一個整數數組 prices,其中第 i 個元素代表了第 i 天的股票價格 ;非負整數 fee 代表了交易股票的手續費用。

你可以無限次地完成交易,但是你每筆交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。

返回獲得利潤的最大值。

注意:這裏的一筆交易指買入持有並賣出股票的整個過程,每筆交易你只需要爲支付一次手續費。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

大體思路:

直接在k=無窮上改就行,由於每筆交易都含手續費,因此在賣的時候加上手續費就行,轉移方程如下:

dp0[i] = max(dp0[i - 1], dp1[i - 1] + prices[i] - fee)

dp1[i] = max(dp1[i - 1], dp0[i - 1] - prices[i])

初值 dp0[0] = 0, dp1[0] = - prices[0]

代碼如下:

    public int maxProfit(int[] prices, int fee) {
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length];
        int[] dp1 = new int[prices.length];
        dp1[0] = -1 * prices[0];
        for(int i = 1; i < prices.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i] - fee);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 1] - prices[i]);
        }
        return dp0[dp0.length - 1];
    }

問題六:最佳股票買賣時機含冷凍期(leetcode309)

問題描述:

給定一個整數數組,其中第 i 個元素代表了第 i 天的股票價格 。​

設計一個算法計算出最大利潤。在滿足以下約束條件下,你可以儘可能地完成更多的交易(多次買賣一支股票):

你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
賣出股票後,你無法在第二天買入股票 (即冷凍期爲 1 天)。
示例:

輸入: [1,2,3,0,2]
輸出: 3 
解釋: 對應的交易狀態爲: [買入, 賣出, 冷凍期, 買入, 賣出]

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

大體思路:

由於當天賣出股票後第二天不能直接買入,也就是說只有前天沒持有,昨天沒持有,今天才可以進行買入。傳遞函數如下:

dp0[i] = max(dp0[i - 1], dp1[i - 1] + prices[i])

dp1[i] = max(dp1[i - 1], dp0[i - 2] - prices[i])

由於前天沒有持有,昨天也沒有持有,今天買入之前的收益是等於前天的收益的。因此爲dp0[i - 2] - prices[i]

由於用到i- 2,初值除了賦i= 0,還應對 i = 1賦值。

        dp0[0] = 0;

        dp1[0] = 0 - prices[0];

        dp0[1] = Math.max(dp0[0], dp1[0] + prices[1]);

        dp1[1] = Math.max(dp1[0], dp0[0] - prices[1]);// 由於-1時坑定沒賣出,因此爲dp0[0] - prices[1]。

實現代碼如下:

    public int maxProfit(int[] prices) {
        // return dfs(prices, 0, 0, 0, 0);
        if(prices.length <= 1){
            return 0;
        }
        int[] dp0 = new int[prices.length]; //dp0[i] 爲i天不持有股票的最大收益
        int[] dp1 = new int[prices.length]; //
        dp0[0] = 0;
        dp1[0] = 0 - prices[0];
        dp0[1] = Math.max(dp0[0], dp1[0] + prices[1]);
        dp1[1] = Math.max(dp1[0], dp0[0] - prices[1]);
        for(int i = 2; i < prices.length; i++){
            dp0[i] = Math.max(dp0[i - 1], dp1[i - 1] + prices[i]);
            dp1[i] = Math.max(dp1[i - 1], dp0[i - 2] - prices[i]);
        }
        return dp0[prices.length - 1];

    }

 

 

 

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