代码随想录算法训练营第四十一天 | 买卖股票的最佳时机进阶版

LeetCode 188.买卖股票的最佳时机Ⅳ:

文章链接
题目链接:买卖股票的最佳时机Ⅳ

思路:

这道题思路与买卖股票的最佳时机Ⅲ相似,那道题的限定股票最多买卖次数为2,这道题是限定股票最多买卖次数为k,需要将原来4个状态扩展为k个持有和k个不持有的状态
动规五部曲:

  • dp数组及含义:
    dp[i][j]:第 i 天状态 j 时所拥有的收益。
    和之前题目思路一样,
    dp[i][0]是第一次持有(也可以dp[i][0]是不操作,那么递推公式也有变化)
    dp[i][1]是第一次不持有
    dp[i][2]是第二次持有
    dp[i][3]是第二次不持有

    dp[i][2k - 2]是第k次持有
    dp[i][2
    k - 1]是第k次不持有
    j 的范围为[0, 2k - 1],数组的列数为 2 k
  • 递推公式:
    dp[i][0] = max(继承之前持有的,之前没持有今天买入)
    = max(dp[i - 1][0], -prices[i])
    dp[i][[1] = max(继承昨天没持有的,之前持有今天卖出)=max(dp[i - 1][1], dp[i - 1][0] + prices[i])
    当j ∈ {2, 4, 6, … 2*k - 2}时
    dp[i][j] = max(继承之前持有的,之前没持有今天买入)=max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i])
    dp[i][j + 1] = max(继承昨天没持有的,之前持有今天卖出)=max(dp[i - 1][j + 1], dp[i - 1][j] + prices[i])
    PS:如果dp数组定义时,dp[i][0]是不操作,那么第一次持有和不持有的公式与后面的相同
  • 初始化:
    由递推公式可知,要初始化第0行,而第0行就是第1天,如果持有股票那么一定是今天买入了,收益为-prices[0];没持有股票可能今天买入又卖出了,收益为0;(后面第 i 天持有股票不一定是第 i 天买入了
    又观察dp数组的列规律得,j % 2 = 0的列都是持有,j % 2 = 1的列都是不持有。因此将持有的列初始化为-prices[0]
  • 遍历顺序:
    从前往后
  • 举例
    k = 2为例
    在这里插入图片描述
    代码
class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        if len(prices) <= 1:
            return 0
        len_prices = len(prices)
        # 初始化
        dp = [[0] * (2*k) for _ in range(len_prices)]
        for j in range(0, 2*k, 2):
            dp[0][j] = -prices[0]
        # 遍历
        for i in range(1, len_prices):
            # dp[i][0]和dp[i][1]单独修改
            dp[i][0] = max(dp[i - 1][0], -prices[i])    # 第一次持有股票
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i])  # 第一次不持有股票
            for j in range(2, 2*k, 2):
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i])  # 第j/2 + 1次持有
                dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] + prices[i])  # 第j/2+1次不持有
        return dp[len_prices - 1][2*k - 1]
        

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

文章链接
题目链接:309.最佳买卖股票时机含冷冻期

思路:

还是需要分析题目中有几个状态,题目中说有冷冻期,在冷冻期时无法买入股票。

  • 首先
    第 i 天的状态可以分为持有股票和不持有股票
  • 其次
    而因为有冷冻期的原因,不持有股票的状态可以分为如下:
    • 第 i 天当天卖出股票
    • 第 i 天为冷冻期
    • 第 i 天为冷冻期之后一直没有买入股票(买入股票的话会变成持有股票的状态,而买入又卖出的话就进入了新一轮的冷静期)
      动规五部曲:
  • dp数组及含义:
    dp[i][j] 为第 i 天状态 j 所拥有的收益
    • dp[i][0]:第 i 天持有股票
    • dp[i][1]:第 i 天当天卖出股票
    • dp[i][2]:第 i 天为冷冻期
    • dp[i][3]第 i 天为冷冻期之后一直没有买入股票
  • 递推公式:
    ① 状态0,要么昨天就持有股票dp[i - 1][0];要么昨天不持有股票,今天买入股票,要求昨天没有卖出股票(即状态1)dp[i - 1][2] - prices[i],dp[i - 1][3] - prices[i]。max(dp[i - 1][0],dp[i - 1][2] - prices[i],dp[i - 1][3] - prices[i])
    ② 状态1:那么昨天一定持有股票,然后今天卖出,dp[i - 1][0] + prices[i]
    ③ 状态2:昨天一定卖出股票,冷冻期不能操作股票dp[i - 1][1]
    ④ 状态3:昨天要么是冷冻期(状态2),要么是冷冻期之后没有买入股票(状态3)。max(dp[i - 1][2], dp[i - 1][3])
  • 初始化:
    由递推公式可知,应当初始化dp[0]这一行,dp[0][0]初始化为 - prices[0],其余状态感觉不好初始化,如果初始化为某个数,那么第 1 天买入了股票的收益就变成了 x - prices[1],x只有为0才合理,因此其余状态初始化为0
  • 遍历方式
    从前往后
  • 举例:
    在这里插入图片描述
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) <= 1:
            return 0
        len_prices = len(prices)
        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], dp[i - 1][2] - prices[i], dp[i - 1][3] - prices[i])    # 持有
            dp[i][1] = dp[i - 1][0] + prices[i] # 今天卖出
            dp[i][2] = dp[i - 1][1] # 冷冻期
            dp[i][3] = max(dp[i - 1][2], dp[i - 1][3])  # 冷冻期之后一直没买入
        # 在不持有的状态中取最大值
        return max(dp[len_prices - 1][1], dp[len_prices - 1][2], dp[len_prices - 1][3])
        

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

文章链接
题目链接:714.买卖股票的最佳时机含手续费

思路:

相对于买卖股票的最佳时机Ⅱ多了个手续费。
分析题目可知,本题能够进行多轮买卖,买卖时收取手续费,那么相对于买卖股票的最佳时机Ⅱ,多了个手续费fee,区别只在于递推公式。
采取卖股票时才收取手续费。
递推公式:

  • 持有股票,要么昨天就持有;要么昨天不持有,今天买入
    dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i])
  • 不持有股票,要么昨天持有,今天卖出(卖出时记得减去 fee);要么昨天不持有
    dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee)
class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        if len(prices) <= 1:
            return 0
        len_prices = len(prices)
        # 初始化
        dp = [[0] * 2 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)    # 不持有,记得-fee
        return dp[len_prices - 1][1]
        

学习收获:

学习了限定股票交易次数为k、含冷冻期和收手续费的情况。其中交易次数为 k 和收手续费都是在之前股票交易的题目上的改进;含冷冻期需要对不持有的状态做进一步分类:冷冻期前(当天卖出)、冷冻期和冷冻期后一直没买入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值