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

LC309

背景

这个题目纠结了很久,看了很多题解思路,都有没解释清楚的地方

最后终于找到了一个能把思路讲清楚的(听起来没有任何漏洞的)
其他的题解可能答案也是对的,但是省略了一些必要的说明,导致理解起来有gap

太不容易了

可能题目本身也有没说的特别清楚的地方。这里补充一下:

对中间的某一天而言,是否存在除了:买入 / 卖出 / 冷冻期 之外的第四种情况——无操作?
答案是:可以存在。

思路

然后说一下本题的解题思路:
大致就是按照【第i天是否持有股票】分情况讨论,然后使用动态规划。关键在于分情况讨论,要足够清楚!!

两个约束条件:

  1. 买入之后必须先卖出之后才能继续买入
  2. 卖出之后不能马上买入,至少冷冻一天(也可以冷冻一天之后继续不买入,相当于冷冻多天!!!)

分情况讨论

# 步骤1: 考虑第 i 天,总共存在两种状态:持有股票 / 不持有 股票,分别记做 dp[i][0],dp[i][1]。i=0时,对应的利润分别为:-prices[0]、0

dp[i]表示第i天的最大利润,dp[i] = max(dp[i][0],dp[i][1])

# 步骤2: 根据第i天的状态,考虑第 i-1天从什么状态 转换得到 第 i天的状态 
对于 dp[i][0] 再分两种情况:
	1)第i-1天不持有(冷冻期/无操作),注意!!此时第i-2天肯定是不持有,否则要么变成2)的情况重复考虑,要么在i-1天卖出,导致第i天处于冷冻期而无法持有(不能得到dp[i][0]的状态) 
	2)第i-1天持有 【第i-2天可以不持有,也可以不持有】

对于 dp[i][1] 也分两种情况:
	3)第i-1天持有,第i天卖出【第i-2天可以持有,也可以不持有】
	4)第i-1天也不持有,第i天无操作【第i-2天可以持有,也可以不持有】

为什么其他3种情况:2)、3)、4)不考虑第i-2天的情况??
因为其他三种情况对第i-2天没有要求!
合法的状态流转顺序包括:
2)持有/不持有->持有->持有; 
3)持有/不持有->持有->不持有;
4)持有/不持有->不持有->不持有;

唯一不合法的是:持有 -> 不持有 -> 持有 ,对应情况1)!!! 
所以情况1肯定是:不持有->不持有->持有

上述四种情况分别对应的状态转移方式和对应的第i天的利润分别为:

1)dp[i-1][1] -> dp[i][0],dp[i-2][1]-prices[i] # 注意这里需要考虑第i-2天的约束!!而不是 dp[i-1][1]-prices[i]

2)dp[i-1][0] -> dp[i][0],dp[i-1][0]
3)dp[i-1][0] -> dp[i][1],dp[i-1][0]+ prices[i]
4)dp[i-1][1] -> dp[i][1],dp[i-1][1]

代码

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n < 2: return 0

        dp = [[0,0] for _ in range(n)]
        # dp[0][0]表示持有,dp[0][1]表示不持有 
        dp[0][0] = -prices[0]
        dp[0][1] = 0
        # 因为下标从2开始,所以需要计算下标为0和1处的初始值,for循环从2到n-1
        # 另一种写法是:数组长度设为n+1,只计算下标为1的初始值,for循环从2到n。减少了初始值计算但是可读性没有前一种强
        dp[1][0] = max(dp[0][1]-prices[1], dp[0][0])
        dp[1][1] = max(dp[0][0]+prices[1], dp[0][1])

        for i in range(2,n): # 注意i-2,需要从2开始循环
            dp[i][0] = max(dp[i-2][1]-prices[i],dp[i-1][0])
            dp[i][1] = max(dp[i-1][0]+prices[i],dp[i-1][1])
        # print(dp)
        return dp[n-1][1]

总结

解题的关键点:

  1. 中间的一天可以有四种状态:买入、卖出、冷冻期、无操作,而不是三种
  2. 仅按照 持有 和 不持有 两种情况去确定每一天的状态,没有其他情况。其中持有内在的包含了:买入、继续持有(无操作);不持有包含了:卖出、继续不持有(无操作)、冷冻期。只按照前述两种状态划分使问题更加简化,避免了同时考虑所有情况导致思路错乱
  3. 在状态转移:考虑从第i-1天状态转移到第i天的时候,需要特别考虑:从第i-1天不持有转移到第i天持有的情况,这种情况要求第i-2天也不持有。其他三种状态转移,都对第i-2天的状态没有要求
  4. for循环递推状态的时候,需要注意边界处理(从第2天开始),初始值(第0天和第1天)的计算。
  5. 最后一天肯定是不持有的,否则无法获得最大利润

猜你喜欢

转载自blog.csdn.net/qxqxqzzz/article/details/129964112