107. Leetcode 123. 买卖股票的最佳时机 III (动态规划-股票交易)

 

步骤一、确定状态:

确定dp数组及下标含义

dp[i] 表示的是在第i天可以获取的最大利润,每天会有持有股票和不持有股票两种状态,这个是 第二维度,还是用0和1表示。 而对于每一种状态,这里还会有交易次数的记录0次or1次or2次, 这个是第三维度。所以dp[天数][当前是否持股][卖出的次数]

步骤二、推断状态方程:

dp[i][0],也就是没有持股的状态,会有三次交易次数讨论:

dp[i][0][0]: 表示的是当前未持股, 且交易了0次,说明目前是从未进行买卖, 那么此时最 大利润为 dp[i][0][0]=0

dp[i][0][1]: 表示的是当前未持股,且有1次交易时最大利润,卖出过1次股票,在第1次卖 出的状态,那么它的状态依然来自两个方向推导过来的,可能是今天卖出去的,也可能是 延续了昨天的状态:

如果是延续了昨天的状态, 那么最大利润就是dp[i-1][0][1]

如果是今天刚卖出去的, 说明昨天是持有股票的,也就是昨天没有卖出(之前交易了0次), 那么最大利润就是dp[i-1][1][0]+prices[i]

所以这时候要选最大, dp[i][0][1] = max(dp[i-1][0][1], dp[i-1][1][0]+prices[i])

dp[i][0][2]: 表示的是当前未持股, 且有2次交易股票时最大利润,这是第二次卖出的状 态,状态依然是两个方向推导过来,可能是今天卖出去的,也可能延续了前面的状态:

如果是延续了昨天的状态, 那么最大利润就是dp[i-1][0][2]

如果是今天刚卖出去的, 说明昨天是持有股票的,但是此时已经交易过了1次了,那么 最大利润就是dp[i-1][1][1]+prices[i]

所以这时候要选最大, dp[i][0][2] = max(dp[i-1][0][2], dp[i-1][1][1]+prices[i])

然后是dp[i][1], 也就是持股状态,依然会有三次次交易讨论:

dp[i][1][0]: 表示的是当前持股,且有0次股票交易时的利润,也就是第一次买入的状态, 那么它的状 态依然来自两个方向推导过来的,可能是今天刚买的,也可能是延续了昨天的状态如果是延续了昨天 的状态, 那么最大利润就是dp[i-1][1][0]

如果是今天刚买的, 说明昨天是没有股票的,那么最大利润就是dp[i-1][0][0]-prices[i] 所以这时候要选最大, dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][0][0]-prices[i])

dp[i][1][1]: 表示的是当前持股,且有1次股票交易时的利润,也就是第二次买入的状态,那么它的状 态依然来自两个方向推导过来的,可能是今天刚买的,也可能是延续了昨天的状态如果是延续了昨天 的状态, 那么最大利润就是dp[i-1][1][1]

如果是今天刚买的, 说明昨天是没有股票的,那么最大利润就是dp[i-1][0][1]-prices[i] 所以这时候要选最大, dp[i][1][1] = max(dp[i-1][1][1], dp[i-1][0][1]-prices[i])

dp[i][1][2]: 表示的是当前持股, 且有2次股票交易时的利润,这个情况是不可能出现的,因为进行 完两笔交易就不能交易了呀,这时候可以给个最小的负数即可。

步骤三、规定初始条件:

初始条件:

全局初始化为0, 然后第0天的所有状态必须都初始化出来: dp[0][0][0] = 0: 开始啥也没干, 利润0

dp[0][0][1] = float("-inf"): 第0天没有持有股票,且进行了一次交易,这种情况不 可能

dp[0][0][2] = float("-inf"):第0天没有持有股票,且进行了两次交易,这种情况依 然不可能

dp[0][1][0] = -prices[0]: 第0天持有股票,且进行了0次交易,这是第一次买入,利 润-prices[0]

dp[0][1][1] = float("-inf"): 第0天持有股票,且进行了1次交易,这种情况不可能 dp[0][1][2] = float("-inf"): 第0天持有股票,且进行了2次交易,这种情况不可能

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 异常判断
        if len(prices) == 1:
            return 0

        # dp[天数][当天是否持股][卖出的次数] 最大利润
        # 当天是否持股: 0 or 1
        # 卖出的次数: 0、1、2
        dp = [[[0 for _ in range(3)] for _ in range(2)] for _ in range(len(prices))]
        dp[0][0][0], dp[0][0][1], dp[0][0][2] = 0, float("-inf"), float("-inf")
        dp[0][1][0], dp[0][1][1], dp[0][1][2] = -prices[0], float("-inf"), float("-inf")

        for i in range(1, len(prices)):
            # 不持有股票
            dp[i][0][0] = 0
            dp[i][0][1] = max(dp[i-1][0][1], dp[i-1][1][0] + prices[i]) # 第一次卖出
            dp[i][0][2] = max(dp[i-1][0][2], dp[i-1][1][1] + prices[i]) # 第二次卖出

            # 持有股票
            dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][0][0] - prices[i]) # 第一次买入
            dp[i][1][1] = max(dp[i-1][1][1], dp[i-1][0][1] - prices[i]) # 第二次买入
            dp[i][1][2] = float("inf") # 第三次买入不可能

        # 返回最后一天未持有股票的值
        return max(dp[-1][0])


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值