代码随想录day43|123买卖股票的最佳时机II188买卖股票的最佳时机IV|01笔记

79 阅读2分钟
  • 123买吗股票的最佳时机

  • 123
  • 第一印象

  • 自己简单的想法是先找一次买卖收益最高的。然后,将这两个元素从集合中去掉,再找下一次。但是题目的要求是,同时只能参与一次交易,而上面的方法会存在同时持有两次交易的情况。所以不可行。
  • 所以如果按照动态规划的思想。那么我们就应该讨论是否进行过交易或正在进行交易的情况。
  • 讲解观后感

  • 首先我们需要定义dp数组的含义。
  • 我们用一个下标来表示天数,一个下标来表示状态。
  • 一天一共就有五个状态,
    • 没有操作 (其实我们也可以不设置这个状态)
    • 第一次持有股票
    • 第一次不持有股票
    • 第二次持有股票
    • 第二次不持有股票
  • dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。
  • 我们根据状态来意义对应递推公式。
    • dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);dp[i][1]取当天买入和保持过去买入的较大值。
    • dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2]do[i][2]取当天卖出或保持过去卖出的较大值
    • 同理可推出剩下状态部分:
      dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
      dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
  • 数组的初始化
    • 第一天没有买入或卖出的情况,值一定都为0。不论第几次。买入都是-price[0].
  • 遍历顺序从前向后即可
  • 解题代码

  •     func maxProfit(prices []int) int {
            dp := make([][]int, len(prices))
            for i := 0; i < len(prices); i++ {
                dp[i] = make([]int, 5)
            }
            dp[0][0] = 0
            dp[0][1] = -prices[0]
            dp[0][2] = 0
            dp[0][3] = -prices[0]
            dp[0][4] = 0
            for i := 1; i < len(prices); i++ {
                dp[i][0] = dp[i-1][0]
                dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
                dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i])
                dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i])
                dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
            }
            return dp[len(prices)-1][4]
        }
        func max(a, b int) int {
            if a > b {
                return a
            }
            return b
        }
    
  • 最后收益最高的情况一定是第二次卖出的,所以返回dp[i][4]
  • 买卖股票的最佳时机IV

  • 188
  • 第一印象

  • 本题在III的基础上,出现了k值的变化。无法按照之前根据数值设定状态转移公式的方法,因为无法有一个确定数值。
  • 讲解观后感

  • 第一印象是无法有确定的数值的。但是看卡尔的解决方案,其实还是可以根据k值创建状态集合以及状态转移公式集合。只要分清奇偶就可。
  • 还是最先确定dp数组以及下标的含义,使用二维数组dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]。我们发现从0开始表示默认,那j奇数时可以表示卖出。题目要求是至多有K笔交易,那么j的范围就定义为 2 * k + 1 就可以了。所以dp数组定义为

    ```
    		   	dp := make([][]int, len(prices))
    		      for i:=0;i<len(dp);i++ {
    		          tmp :=make([]int, 2*k + 1)
    		          dp[i] = tmp
    		      }
    ```
    
  • 确定转移公式。
    • 达到dp[i][1]状态,有两个具体操作:
      • 操作一:第i天买入股票了,那么dp[i][1] = dp[i - 1][0] - prices[i]

      • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

        选最大的,所以 dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1])

    • 同理dp[i][2]也有两个操作:
      • 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]

      • 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

        所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

  • 我们根据一和二的情况来代表奇偶的情况
  •     for (int j = 0; j < 2 * k - 1; j += 2) {
            dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
            dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
        }
    
  • 初始化dp数组:我们还是按照奇偶的情况来代表第一天买入和卖出。在第一天未买入和卖出都保持0即可。

    ```
    		  for j := 1; j < 2 * k; j += 2 { //初始dp数组
    		          dp[0][j] = -prices[0] //买入的情况
    		      }
    ```
    
  • 我们还是按照从前到后的顺序推导
  • 解题代码

  •     // 买卖股票的最佳时机IV 动态规划
        // 时间复杂度O(kn) 空间复杂度O(kn)
        func maxProfit(k int, prices []int) int {
            if k == 0 || len(prices) == 0 {
                return 0
            }
            
            dp := make([][]int, len(prices))
            for i:=0;i<len(dp);i++ {
                tmp :=make([]int, 2*k + 1)
                dp[i] = tmp
            }
            for j := 1; j < 2 * k; j += 2 { //初始dp数组
                dp[0][j] = -prices[0]
            }
            
            for i := 1; i < len(prices); i++ {
                for j := 0; j < 2 * k; j += 2 {
                    dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i])
                    dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i])
                }
            }
            return dp[len(prices) - 1][2 * k]
        }
        
        func max(a, b int) int {
            if a > b {
                return a
            }
            return b
        }