代码随想录算法训练营第五十一天 |309. 最佳买卖股票时机含冷冻期、714. 买卖股票的最佳时机含手续费

82 阅读3分钟

309. 最佳买卖股票时机含冷冻期

代码随想录文章讲解

动态规划

  • 四个状态:

    • 0: 买入状态
    • 1: 保持卖出状态
    • 2: 今天卖出状态
    • 3: 处于冷冻期状态
  • 递推公式:

    • 达到买入股票状态(状态一)即:dp[i][0],有两个具体操作:

      • 操作一:前一天就是持有股票状态(状态一),dp[i][0] = dp[i - 1][0]

      • 操作二:今天买入了,有两种情况

        • 前一天是冷冻期(状态四),dp[i - 1][3] - prices[i]
        • 前一天是保持卖出股票状态(状态二),dp[i - 1][1] - prices[i]

      所以操作二取最大值,即:max(dp[i - 1][3], dp[i - 1][1]) - prices[i]

      那么dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);

    • 达到保持卖出股票状态(状态二)即:dp[i][1],有两个具体操作:

      • 操作一:前一天就是状态二
      • 操作二:前一天是冷冻期(状态四)

      dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);

    • 达到今天就卖出股票状态(状态三),即:dp[i][2],只有一个操作:

      • 操作一:昨天一定是买入股票状态(状态一),今天卖出

      即:dp[i][2] = dp[i - 1][0] + prices[i];

    • 达到冷冻期状态(状态四),即:dp[i][3],只有一个操作:

      • 操作一:昨天卖出了股票(状态三)

      dp[i][3] = dp[i - 1][2];

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) == 1:
            return 0
        
        dp = [ [0]*4 for _ in range(len(prices)) ]
        dp[0][0] = -prices[0]
​
        for i in range(1, len(prices)):
            dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1])-prices[i])
            dp[i][1] = max(dp[i-1][1], dp[i-1][3])
            dp[i][2] = dp[i-1][0] + prices[i]
            dp[i][3] = dp[i-1][2]
        
        return max(dp[-1][1], dp[-1][2], dp[-1][3])

714. 买卖股票的最佳时机含手续费

代码随想录文章讲解

动态规划

  • 和122. 买卖股票的最佳时机 II一致,不过在售出时要减去fee
class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        if len(prices) == 1:
            return 0
        
        dp = [ [0, 0] for _ in range(len(prices))]
        dp[0][0] = -prices[0]
​
        for i in range(1, len(prices)):
            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)
        
        return dp[-1][1]

贪心

  • 使用贪心策略,就是最低值买,最高值(如果算上手续费还盈利)就卖。

  • 买入日期:其实很好想,遇到更低点就记录一下。

  • 卖出日期:这个就不好算了,但也没有必要算出准确的卖出日期,只要当前价格大于(最低价格+手续费),就可以收获利润,至于准确的卖出日期,就是连续收获利润区间里的最后一天(并不需要计算是具体哪一天)。

  • 所以我们在做收获利润操作的时候其实有三种情况:

    • 情况一:收获利润的这一天并不是收获利润区间里的最后一天(不是真正的卖出,相当于持有股票),所以后面要继续收获利润。
    • 情况二:前一天是收获利润区间里的最后一天(相当于真正的卖出了),今天要重新记录最小价格了。
    • 情况三:不作操作,保持原有状态(买入,卖出,不买不卖)
class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        min_price = prices[0]
        result = 0
        for i in range(1, len(prices)):
            if prices[i] < min_price:
                min_price = prices[i]
            
            if prices[i] > min_price + fee:
                result += prices[i] - min_price - fee
                min_price = prices[i] - fee
                
        return result