【動態規劃】_leetcode刷題的各種股票問題

1.leetcode_121.買賣股票的最佳時機

  給定一個數組,它的第 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。

2.解題思路

方法1:

在這裏插入圖片描述

方法2:動態規劃

dp[i][0]代表第i+1天沒有持股票,dp[i][1]代表第i+1天持有股票

  • 初始化:
    • dp[0][0] = 0;       第一天沒有持股,這時候相當於沒有買入,故爲0
      dp[0][1] = -prices[0];  第一天持股,相當於買入,這時候爲-pricrs[0]
  • 狀態轉移方程:
    • 沒有持股 =max(昨天沒有持股今天維持現狀 , 昨天持股,今天賣出)
             dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i]);
    • 持股 = max(昨天持股今天維持現狀,(之前沒有交易,今天買入))【因爲只能完成一筆交易,故今天買入,則前面就相當於一直沒有進行交易爲利潤爲0】
           dp[i][1] = Math.max(dp[i-1][1], -1 * prices[i])

3.代碼

方法1:

 public static int maxProfit(int[] prices) {
       int minPrice = prices[0];
       int maxProfit = 0;
       for(int i = 1;i<prices.length;i++){
           if(minPrice>prices[i]){
               minPrice = prices[i];
           }else if(maxProfit<prices[i] - minPrice){
               maxProfit = prices[i] - minPrice;
           }
       }
       return maxProfit;
   }

方法2:

class Solution {
   public static int maxProfit(int[] prices) {
       if(prices.length == 0)
           return 0; 
       int dp[][] = new int[prices.length][2];
       dp[0][0] = 0;
        dp[0][1] = -prices[0];
       //求第(i+1)天持股或者不持股的最大收益
       for(int i = 1;i<dp.length;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], -1 * prices[i]);
       }
       //最大利益肯定是那天沒有持股時的利益
       return dp[dp.length-1][0];
   }
}

拓展1

			以上題目中的“只允許完成一筆交易” 變成 “可以完成無數比交易”

同:leetcode-122:買賣股票的最佳時機 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/submissions/
動態規劃:

dp[i][0]代表第i+1天沒有持股票,dp[i][1]代表第i+1天持有股票

  • 初始化:
    • dp[0][0] = 0;        第一天沒有持股,這時候相當於沒有買入,故爲0
    • dp[0][1] = -prices[0];   第一天持股,相當於買入,這時候爲-prices[0]
  • 狀態轉移方程:
    • 沒有持股 =max(昨天沒有持股今天維持現狀 , 昨天持股,今天賣出)
         dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i]); //
    • 持股 = max(昨天持股今天維持現狀,(昨天沒有持股,今天買入))【相對於之前,這裏改變了】
          dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i])

代碼

public static int maxProfit(int[] prices) {
 	    if(prices.length == 0)
            return 0; 
        int dp[][] = new int[prices.length][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        //求第(i+1)天持股或者不持股的最大收益
        for(int i = 1;i<dp.length;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], dp[i-1][0] - prices[i]);
        }
        //最大利益肯定是那天沒有持股時的利益
        return dp[dp.length-1][0];
    }

拓展2:

		在拓展1的基礎上[可以完成無數比交易],每次 sell 之後要等一天才能繼續交易。

同:leetcode-309:最佳買賣股票時機含冷凍期 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
動態規劃:

dp[i][0]代表第i+1天沒有持股票,dp[i][1]代表第i+1天持有股票

  • 初始化:
    • dp[0][0] = 0;                     //第1天沒有持股,這時候相當於沒有買入,故爲0
    • dp[1][0] =Math.max(0,prices[1]-prices[0]); //第2天沒有持股,這時候max(第一天沒持股,第一天持股第二天賣出)
    • dp[0][1] = -prices[0];                //第1天持股維持,相當於買入,這時候爲-prices[0]
    • dp[1][1] = Math.max(-prices[0],-prices[1]);//第2天持股,這時候max(第一天持股維持,第一天沒持股第二天買入)
  • 狀態轉移方程:
    • 沒有持股 =max(昨天沒有持股今天維持現狀 , (昨天持股,今天賣出))
          dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i]);
    • 持股 = max(昨天持股今天維持現狀,(前天沒有持股,今天買入))即:第i天要買的時候,要從前天的狀態進行判斷
         dp[i][1] = Math.max(dp[i-1][1], dp[i-2][0] - prices[i]);
代碼
 public static int maxProfit4(int[] prices) {
      if(prices.length == 0 ||prices.length == 1)
           return 0;
       int dp[][] = new int[prices.length][2];
       //初始化
       dp[0][0] = 0;
       dp[1][0] =Math.max(0,prices[1]-prices[0]);
       dp[0][1] = -prices[0];
       dp[1][1] = Math.max(-prices[0],-prices[1]);
       //動態規劃
       for(int i = 2;i<dp.length;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], dp[i-2][0] - prices[i]);
       }
       //最大利益肯定是那天沒有持股時的利益
       return dp[dp.length-1][0];
   }
拓展3:
				每次買入交易要支付手續費。

leetcode-714:買賣股票的最佳時機含手續費 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
動態規劃

dp[i][0]代表第i+1天沒有持股票,dp[i][1]代表第i+1天持有股票

  • 初始化:
    • dp[0][0] = 0;         第一天沒有持股,這時候相當於沒有買入,故爲0
      dp[0][1] = -prices[0] - fee;   第一天持股,相當於買入,這時候爲-prices[0] - fee ,本次以買入作爲交易的開始,每次買入就扣除手續費,賣出的時候就不扣除手續費了。
  • 狀態轉移方程:
    • 沒有持股 =max(昨天沒有持股今天維持現狀 , 昨天持股,今天賣出)【買入扣除手續費,賣出就不扣除了,因爲買賣都完成才扣一次手續費】
          dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i]);
    • 持股 = max(昨天持股今天維持現狀,(昨天沒有持股,今天買入))
          dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i] - fee);
代碼
public static int maxProfit5(int[] prices,int fee) {
       if(prices.length == 0)
           return 0;
       int dp[][] = new int[prices.length][2];
       //初始化
       dp[0][0] = 0;
       dp[0][1] = -prices[0]-fee;
       //求第(i+1)天持股或者不持股的最大收益
       for(int i = 1;i<dp.length;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], dp[i-1][0] - prices[i] - fee);
       }
       //最大利益肯定是那天沒有持股時的利益
       return dp[dp.length-1][0];
   }
拓展4:
			 以上題目中的“只允許完成一筆交易” 變成 “只允許完成2筆交易”

同: https://www.nowcoder.com/questionTerminal/3e8c66829a7949d887334edaa5952c28
已調試通過
同: leetcode-123. 買賣股票的最佳時機 III :https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/submissions/
動態規劃

dp[i][k][0]代表第i+1天交易k次並且沒有持股票,dp[i][k][1]代表第i+1天交易k次並且持有股票

  • 初始化:
    • dp[0][1][0] = 0;      //第一天,交易次數爲1,沒持股,相當於就沒買入
    • dp[0][1][1] = -prices[0]; //第一天,交易次數爲1,持股,相當於買入股票
    • dp[0][2][0] = 0;     //第一天,交易次數爲2,沒持股,相當於買入賣出買入賣出,沒有盈利
    • dp[0][2][1] = -prices[0]; //第一天,交易次數爲2,持股,相當於買入再賣出(交易1次)再買入
  • 狀態轉移方程:
    • 第(i+1)天交易數爲2,沒持股 = max(前一天交易數爲2沒持股維持,前一天交易數爲2持股今天賣出)
          dp[i][2][0] = Math.max(dp[i-1][2][0],dp[i-1][2][1] + prices[i]);
    • 第(i+1)天交易數爲2,持股 = 前一天交易數爲2持股維持,前一天交易數爲1沒持股今天買入[買入之前必須賣出,故之前交易數應該爲1而不是爲2]
         dp[i][2][1] = Math.max(dp[i-1][2][1],dp[i-1][1][0] - prices[i]);
    • 第(i+1)天交易數爲1,沒持股 = max(前一天交易數爲1沒持股維持,前一天交易數爲1持股今天賣出)[賣出之前必須買入,故交易數爲1而不是0]
         dp[i][1][0] = Math.max(dp[i-1][1][0],dp[i-1][1][1] + prices[i]);
    • 第(i+1)天交易數爲1,持股 = max(前一天交易數爲1持股維持,(沒交易,今天買入))
          dp[i][1][1] = Math.max(dp[i-1][1][1],-1 * prices[i]);
import java.util.*;
public class Stock {
   public int maxProfit(int[] prices, int n) {
   	  if(prices.length == 0)
             return 0;
       //dp[i][k][0]代表第(i+1)天交易次數爲k,沒持股
       int dp[][][] = new int[prices.length][3][2];
       //初始化
       dp[0][1][0] = 0;  
       dp[0][1][1] = -prices[0];  
       dp[0][2][0] = 0;   
       dp[0][2][1] = -prices[0];
   	    //動態規劃
       for(int i = 1;i<prices.length;i++){
           dp[i][2][0] = Math.max(dp[i-1][2][0],dp[i-1][2][1] + prices[i]);
           dp[i][2][1] = Math.max(dp[i-1][2][1],dp[i-1][1][0] - prices[i]);
           dp[i][1][0] = Math.max(dp[i-1][1][0],dp[i-1][1][1] + prices[i]);
           dp[i][1][1] = Math.max(dp[i-1][1][1],-1 * prices[i]);
       }
       return dp[prices.length-1][2][0];
   }
}

拓展5:

						以上題目中的“只允許完成一筆交易” 變成 “只允許完成k筆交易”

leetcode-188: 買賣股票的最佳時機IV:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/
動態規劃:

dp[i][k][0]代表第i+1天交易k次並且沒有持股票,dp[i][k][1]代表第i+1天交易k次並且持有股票

  • 初始化:
    • dp[0][i][0] = 0; 【i爲交易成交的筆數】即第一天,持續的買入賣出買入賣出,沒收益
    • dp[0][i][1] = -prices[0]; 【i爲交易成交的筆數】即第一天,持續的買入賣出買入,收益爲買入的-prices[0]
  • 狀態轉移方程:
    • 第(i+1)天交易數爲j,沒持股 = max(前一天交易數爲j沒持股維持,前一天交易數爲j持股今天賣出)
          dp[i][j][0] = Math.max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]);
    • 第(i+1)天交易數爲j,持股 = max(前一天交易數爲j持股維持,前一天交易數爲j沒持股今天買入)
          dp[i][j][1] = Math.max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]);

答案超出內存限制:

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices.length == 0 || k==0)
            return 0;
        int dp[][][] = new int[prices.length][k+1][2];
       //初始化
       for(int i = 1;i<=k;i++)
       {
           dp[0][i][0] = 0;   //第一天,買入賣出買入賣出,沒收益
           dp[0][i][1] = -prices[0]; 
       }
         //求第(i+1)天持股或者不持股的最大收益
        for(int i = 1;i<dp.length;i++){
            for(int j = k;j >= 1;j --) {
                dp[i][j][0] = Math.max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]);
                dp[i][j][1] = Math.max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]);
            }
        }
        //最大利益肯定是那天沒有持股時的利益
        return dp[dp.length-1][k][0];
    }
}

有時間再想想,覺得思路應該是對的,但是這些題應該有更優的解法,動態規劃時間複雜度還是有點高的。
整理自:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-l-3/

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