带状态的DP君~
-  
类型总结:买卖一次、买卖无限次、买卖k次、买卖无限次、含冷冻期。
 -  
买卖k次的问题需要不断统计、维护买卖i次的最大收益。
 -  
状态较多的题可以借助状态机分析状态转移情况。
 
121 买卖股票的最佳时机
统计第 i i i天之前的股票最低价格,计算在第 i i i天卖出时能得到的最大收益,从而得到全局的最大收益。一次遍历可以解决。
class Solution {
    public int maxProfit(int[] prices) {
        // 目前的最小价格
        int minPrice = prices[0];
        // 能够获得的最大收益
        int profit = 0;
        for (int i = 1; i < prices.length; i++) {
            // 更新收益
            profit = Math.max(profit, prices[i] - minPrice); 
            // 更新价格
            minPrice = Math.min(minPrice, prices[i]);
        }
        return profit;
    }
}
 
122 买卖股票的最佳时机Ⅱ
贪心(不好意思啦作弊啦),如果当前股票价格比前一天高,那就在前一天买入、在今天卖出。
class Solution {
    public int maxProfit(int[] prices) {
        int profit = 0;
        
        for (int i = 1; i < prices.length; i++) {
            profit += Math.max(0, prices[i] - prices[i - 1]);
        }
        
        return profit;
    }
}
 
123 买卖股票的最佳时机Ⅲ ★
思路可能不难,但细节确实太多。
以第一次买入、第一次卖出、第二次买入、第二次卖出作为4个状态。
先考虑在第i天时的情况
D[i][0] 在第i天第一次买入股票的收益: -prices[i]
D[i][1] 在第i天第一次卖出股票的最大收益: prices[i] - min(prices[j]), j <= i
D[i][2] 在第i天第二次买入股票的最大收益: -prices[i] + max(D[j][1]), j <= i
D[i][3] 在第i天第二次卖出股票的最大收益: prices[i] + max(D[j][2]), j <= i
 
在考虑在第i天及之前的情况
需要的统计量包括:
1、第 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i天之前股票的最小价格
  
      
       
        
        
          minPrice 
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          = 
         
        
          min 
         
        
           
         
        
          ( 
         
        
          minPrice 
         
        
          ( 
         
        
          i 
         
        
          − 
         
        
          1 
         
        
          ) 
         
        
          , 
         
        
            
         
        
          p 
         
        
          r 
         
        
          i 
         
        
          c 
         
        
          e 
         
        
          s 
         
        
          [ 
         
        
          i 
         
        
          ] 
         
        
          ) 
         
        
       
         \text{minPrice}(i) = \min(\text{minPrice}(i - 1),\ prices[i]) 
        
       
     minPrice(i)=min(minPrice(i−1), prices[i])
 2、第 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i天及之前第一次卖出股票能得到的最大收益
- 可以在同一天先买入后卖出,至少这样第一笔交易不会是负收益,故减去 m i n P r i c e ( i ) minPrice(i) minPrice(i),而不是减去 m i n P r i c e ( i − 1 ) minPrice(i - 1) minPrice(i−1)
 
sell 1 ( i ) = max  ( sell 1 ( i − 1 ) , p r i c e s [ i ] − minPrice ( i ) ) \text{sell}_1(i) = \max(\text{sell}_1(i-1),\ prices[i] - \text{minPrice}(i)) sell1(i)=max(sell1(i−1), prices[i]−minPrice(i))
3、第 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i天及之前第二次买入股票能得到的最大收益
  
      
       
        
         
         
           buy 
          
         
           2 
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          = 
         
        
          max 
         
        
           
         
        
          ( 
         
         
         
           buy 
          
         
           2 
          
         
        
          ( 
         
        
          i 
         
        
          − 
         
        
          1 
         
        
          ) 
         
        
          , 
         
        
            
         
        
          − 
         
        
          p 
         
        
          r 
         
        
          i 
         
        
          c 
         
        
          e 
         
        
          s 
         
        
          [ 
         
        
          i 
         
        
          ] 
         
        
          + 
         
         
         
           sell 
          
         
           1 
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          ) 
         
        
       
         \text{buy}_2(i) = \max(\text{buy}_2(i-1),\ -prices[i] + \text{sell}_1(i)) 
        
       
     buy2(i)=max(buy2(i−1), −prices[i]+sell1(i))
 4、第 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i天及之前第二次卖出股票能得到的最大收益:
  
      
       
        
         
         
           sell 
          
         
           2 
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          = 
         
        
          max 
         
        
           
         
        
          ( 
         
         
         
           sell 
          
         
           2 
          
         
        
          ( 
         
        
          i 
         
        
          − 
         
        
          1 
         
        
          ) 
         
        
          , 
         
        
            
         
        
          p 
         
        
          r 
         
        
          i 
         
        
          c 
         
        
          e 
         
        
          s 
         
        
          [ 
         
        
          i 
         
        
          ] 
         
        
          + 
         
         
         
           buy 
          
         
           2 
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          ) 
         
        
       
         \text{sell}_2(i) = \max(\text{sell}_2(i-1),\ prices[i] + \text{buy}_2(i)) 
        
       
     sell2(i)=max(sell2(i−1), prices[i]+buy2(i))
 写成代码类似物,变量的更新方式如下:
第i天及之前:
股票的最小价格:buy1 = min(buy1, prices[i])
第一次卖出股票能得到的最大收益:sell1 = max(sell1, prices[i] - buy1)
第二次买入股票的最大收益:buy2 = max(buy2, -prices[i] + sell1)
第二次卖出股票能获得的最大收益:sell2 = max(sell2, prices[i] + buy2)
 
完整代码,代码非常简介,分析非常复杂:
class Solution {
    public int maxProfit(int[] prices) {
        int minPrice = Integer.MAX_VALUE;
        int sell1 = Integer.MIN_VALUE;
        int buy2 = Integer.MIN_VALUE;
        int sell2 = Integer.MIN_VALUE;
        for (int i = 0; i < prices.length; i++) {
            minPrice = Math.min(prices[i], minPrice);
            sell1 = Math.max(sell1, prices[i] - minPrice);
            buy2 = Math.max(buy2, -prices[i] + sell1);
            sell2 = Math.max(sell2, prices[i] + buy2);
        }
        return sell2;
    }
}
 
188 买卖股票的最佳时机Ⅳ
最多可以买 k k k次啦,建议写完Ⅲ再来写Ⅳ,要不真的思考过程too hard。
第 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i天及之前第k次买入的最大收益:
  
      
       
        
        
          b 
         
        
          u 
         
         
         
           y 
          
         
           k 
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          = 
         
        
          max 
         
        
           
         
        
          ( 
         
        
          b 
         
        
          u 
         
         
         
           y 
          
         
           k 
          
         
        
          ( 
         
        
          i 
         
        
          − 
         
        
          1 
         
        
          ) 
         
        
          , 
         
        
          − 
         
        
          p 
         
        
          r 
         
        
          i 
         
        
          c 
         
        
          e 
         
        
          s 
         
        
          [ 
         
        
          i 
         
        
          ] 
         
        
          + 
         
        
          s 
         
        
          e 
         
        
          l 
         
         
         
           l 
          
          
          
            k 
           
          
            − 
           
          
            1 
           
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          ) 
         
        
       
         buy_k(i) = \max(buy_k(i - 1), -prices[i]+sell_{k-1}(i)) 
        
       
     buyk(i)=max(buyk(i−1),−prices[i]+sellk−1(i))
 第 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i天及之前第k次卖出的最大收益:
  
      
       
        
        
          s 
         
        
          e 
         
        
          l 
         
         
         
           l 
          
         
           k 
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          = 
         
        
          max 
         
        
           
         
        
          ( 
         
        
          s 
         
        
          e 
         
        
          l 
         
         
         
           l 
          
         
           k 
          
         
        
          ( 
         
        
          i 
         
        
          − 
         
        
          1 
         
        
          ) 
         
        
          , 
         
        
          p 
         
        
          r 
         
        
          i 
         
        
          c 
         
        
          e 
         
        
          s 
         
        
          [ 
         
        
          i 
         
        
          ] 
         
        
          + 
         
        
          b 
         
        
          u 
         
         
         
           y 
          
         
           k 
          
         
        
          ( 
         
        
          i 
         
        
          ) 
         
        
          ) 
         
        
       
         sell_k(i)=\max(sell_k(i-1), prices[i]+buy_k(i)) 
        
       
     sellk(i)=max(sellk(i−1),prices[i]+buyk(i))
 所以需要一个数组捏:int[][] D = new int[k + 1][2]
遍历整个数组,遍历到每一个元素时,更新D。
D[k][0] = max(D[k][0], -prices[i] + D[k - 1][1])
D[k][1] = max(D[k][1], prices[i] + D[k][0])
 
初始化:
D[k][0] = -inf
D[k][1] = 0
 
完整代码:
class Solution {
    public int maxProfit(int k, int[] prices) {
        // D[j][0]表示第j次买入的最大收益,D[j][1]表示第j次卖出的最大收益,j从1到k
        int[][] D = new int[k + 1][2];
        for (int j = 0; j <= k; j++) {
            D[j][0] = Integer.MIN_VALUE;
        }
        for (int price : prices) {
            for (int j = 1; j <= k; j++) {
                D[j][0] = Math.max(D[j][0], -price + D[j - 1][1]);
                D[j][1] = Math.max(D[j][1], price + D[j][0]);
            }
        }
        return D[k][1];
    }
}
 
309 最佳买卖股票时机含冷冻期
分析不明白了捏,画个状态机看看。
D [ i ] [ 0 ] , D [ i ] [ 1 ] , D [ i ] [ 2 ] D[i][0], D[i][1], D[i][2] D[i][0],D[i][1],D[i][2]表示第 i i i天在未持有、持有、冷冻期状态的最大收益。
- 第 
      
       
        
        
          i 
         
        
       
         i 
        
       
     i天若为持有状态,可由第 
      
       
        
        
          i 
         
        
          − 
         
        
          1 
         
        
       
         i-1 
        
       
     i−1天的 <持有状态 + 不卖动作> 和第 
      
       
        
        
          i 
         
        
          − 
         
        
          1 
         
        
       
         i-1 
        
       
     i−1天的 <未持有状态 + 买入动作> 转移而来 
  
- <持有状态 + 不卖动作> : D [ i − 1 ] [ 1 ] D[i-1][1] D[i−1][1]
 - <未持有状态 + 买入动作>: − p r i c e s [ i ] + D [ i − 1 ] [ 0 ] -prices[i] + D[i-1][0] −prices[i]+D[i−1][0]
 - D [ i ] [ 1 ] = max  ( D [ i − 1 ] [ 1 ] , − p r i c e s [ i ] + D [ i − 1 ] [ 0 ] ) D[i][1] = \max(D[i-1][1], -prices[i] + D[i-1][0]) D[i][1]=max(D[i−1][1],−prices[i]+D[i−1][0])
 
 - 第 
      
       
        
        
          i 
         
        
       
         i 
        
       
     i天若为未持有状态,可由第 
      
       
        
        
          i 
         
        
          − 
         
        
          1 
         
        
       
         i-1 
        
       
     i−1天的 <冷冻期 + 过一天> 和第 
      
       
        
        
          i 
         
        
          − 
         
        
          1 
         
        
       
         i-1 
        
       
     i−1天的 <未持有状态 + 不买动作> 转移而来 
  
- <冷冻期 + 过一天> : D [ i − 1 ] [ 2 ] D[i-1][2] D[i−1][2]
 - <未持有状态 + 不买动作>: D [ i − 1 ] [ 0 ] D[i-1][0] D[i−1][0]
 - D [ i ] [ 0 ] = max  ( D [ i − 1 ] [ 0 ] , D [ i − 1 ] [ 2 ] ) D[i][0] = \max(D[i-1][0], D[i-1][2]) D[i][0]=max(D[i−1][0],D[i−1][2])
 
 - 第 
      
       
        
        
          i 
         
        
       
         i 
        
       
     i天若为冷冻期: 
  
- <持有状态 + 卖出>: D [ i ] [ 2 ] = D [ i − 1 ] [ 1 ] + p r i c e s [ i ] D[i][2] = D[i-1][1]+prices[i] D[i][2]=D[i−1][1]+prices[i]
 
 
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TuEj2YNd-1686482336665)(【Leetcode】DP-股票系列,买卖股票的最佳时机/image-20230611140915336.png)]](https://img-blog.csdnimg.cn/f48468dd9cf7401bafa66dca0a6384bd.png)
代码:
class Solution {
    public int maxProfit(int[] prices) {
        int hold = Integer.MIN_VALUE;
        int freeze = 0;
        int notHold = 0;
        int profit = Integer.MIN_VALUE;
        for (int i = 0; i < prices.length; i++) {
            hold = Math.max(hold, -prices[i] + notHold);
            notHold = Math.max(notHold, freeze);
            freeze = hold + prices[i];
            
            profit = Math.max(profit, freeze);
        }
        return profit;
    }
}
 
714 买卖股票的最佳时机含手续费
和Ⅱ蛮像,状态机确实好使。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eu31b4O6-1686482336666)(【Leetcode】DP-股票系列,买卖股票的最佳时机/image-20230611143118427.png)]](https://img-blog.csdnimg.cn/60679204bb294f6f8a5a25dfcd927b5a.png)
有手续费的话就不会出现当日买入、当日卖出的情况啦,负收益捏。
- 第 
      
       
        
        
          i 
         
        
       
         i 
        
       
     i天为持有状态的最大收益: 
  
- <未持有 + 买入>: − p r i c e s [ i ] + D [ i − 1 ] [ 1 ] -prices[i] + D[i-1][1] −prices[i]+D[i−1][1]
 - <持有 + 不卖>: D [ i − 1 ] [ 0 ] D[i-1][0] D[i−1][0]
 
 - 第 
      
       
        
        
          i 
         
        
       
         i 
        
       
     i天为未持有状态的最大收益: 
  
- <持有 + 卖出>: p r i c e s [ i ] − f e e + D [ i − 1 ] [ 0 ] prices[i] -fee + D[i-1][0] prices[i]−fee+D[i−1][0]
 - <未持有 + 不卖>: D [ i − 1 ] [ 1 ] D[i-1][1] D[i−1][1]
 
 
class Solution {
    public int maxProfit(int[] prices, int fee) {
        int hold = Integer.MIN_VALUE;
        int notHold = 0;
        int profit = 0;
        for (int i = 0; i < prices.length; i++) {
            hold = Math.max(hold, -prices[i] + notHold);
            notHold = Math.max(notHold, prices[i] - fee + hold);
            // 更新最大收益
            profit = Math.max(profit, notHold);
        }
        return profit;
    }
}
                


















