最佳买卖股票时机含冷冻期!algorithm-base动画解析动态规划

最佳买卖股票时机含冷冻期!algorithm-base动画解析动态规划

【免费下载链接】algorithm-base 一位酷爱做饭的程序员,立志用动画将算法说的通俗易懂。我的面试网站 www.chengxuchu.com 【免费下载链接】algorithm-base 项目地址: https://gitcode.com/gh_mirrors/al/algorithm-base

你还在为动态规划问题头疼吗?遇到股票买卖问题是否感到无从下手?本文将通过动画模拟的方式,用通俗易懂的语言带你解决"最佳买卖股票时机含冷冻期"这一经典动态规划问题。读完本文后,你将能够:理解动态规划在股票问题中的应用思路,掌握状态转移方程的推导方法,学会处理含冷冻期的复杂场景。

问题引入

假设你是一位股票投资者,每天可以选择买入或卖出股票,但有一个限制:卖出股票后,你无法在第二天立即买入(即存在一天的冷冻期)。给定一段时间内的股票价格,你需要计算出能获得的最大利润。

例如,股价序列为 [1,2,3,0,2] 时,最佳交易策略是:第0天买入,第1天卖出(获利1),第3天买入,第4天卖出(获利2),总利润3。注意中间必须间隔一天冷冻期。

动态规划基础回顾

动态规划(Dynamic Programming,简称DP)是解决多阶段决策问题的常用方法。它通过将复杂问题分解为重叠子问题,并存储子问题的解来避免重复计算,从而提高效率。在股票问题中,我们通常需要定义不同的"状态"来描述当前的持股情况。

如果你对动态规划基础还不熟悉,可以先阅读【动画模拟】动态规划基础(注:原仓库中动态规划目录下文件为空,实际使用时建议补充基础内容)。

状态定义与转移

状态定义

针对含冷冻期的股票问题,我们需要定义三种状态:

  1. dp[i][0]:第i天结束时,不持有股票且不在冷冻期的最大利润
  2. dp[i][1]:第i天结束时,持有股票的最大利润
  3. dp[i][2]:第i天结束时,不持有股票且处于冷冻期的最大利润

状态转移方程

我们可以通过以下关系推导出状态转移方程:

  • 不持有股票且不在冷冻期dp[i][0]):可以从两种情况转移而来

    • 前一天也不持有股票且不在冷冻期:dp[i-1][0]
    • 前一天处于冷冻期,今天冷冻期结束:dp[i-1][2]
    • 转移方程:dp[i][0] = max(dp[i-1][0], dp[i-1][2])
  • 持有股票dp[i][1]):可以从两种情况转移而来

    • 前一天就持有股票,今天继续持有:dp[i-1][1]
    • 前一天不持有股票且不在冷冻期,今天买入:dp[i-1][0] - prices[i]
    • 转移方程:dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
  • 不持有股票且处于冷冻期dp[i][2]):只能从一种情况转移而来

    • 前一天持有股票,今天卖出:dp[i-1][1] + prices[i]
    • 转移方程:dp[i][2] = dp[i-1][1] + prices[i]

初始条件

  • 第0天持有股票:dp[0][1] = -prices[0]
  • 第0天不持有股票(非冷冻期):dp[0][0] = 0
  • 第0天不可能处于冷冻期:dp[0][2] = 0(或负无穷,实际计算中可设为0)

代码实现

根据上述状态转移方程,我们可以编写如下代码:

def maxProfit(prices):
    if not prices:
        return 0
    
    n = len(prices)
    # 初始化dp数组
    dp = [[0]*3 for _ in range(n)]
    dp[0][1] = -prices[0]
    
    for i in range(1, n):
        dp[i][0] = max(dp[i-1][0], dp[i-1][2])
        dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
        dp[i][2] = dp[i-1][1] + prices[i]
    
    # 最后一天不能持有股票,所以取不持有股票的两种状态的最大值
    return max(dp[n-1][0], dp[n-1][2])

空间优化

观察状态转移方程可以发现,每个状态只与前一天的状态有关,因此我们可以使用三个变量来代替二维数组,将空间复杂度从O(n)优化到O(1):

def maxProfit(prices):
    if not prices:
        return 0
    
    n = len(prices)
    # 初始化状态变量
    dp0, dp1, dp2 = 0, -prices[0], 0
    
    for i in range(1, n):
        new_dp0 = max(dp0, dp2)
        new_dp1 = max(dp1, dp0 - prices[i])
        new_dp2 = dp1 + prices[i]
        dp0, dp1, dp2 = new_dp0, new_dp1, new_dp2
    
    return max(dp0, dp2)

动画模拟演示

下面通过动画模拟来直观理解整个过程(实际使用时建议补充动画图片或GIF):

假设股价序列为 [1,2,3,0,2],我们可以得到如下状态变化:

天数价格不持有(非冷冻)持有不持有(冷冻)
010-10
120max(-1, 0-2)=-1-1+2=1
23max(0,1)=1max(-1, 0-3)=-1-1+3=2
30max(1,2)=2max(-1, 2-0)=22+0=2
42max(2,2)=2max(2, 2-2)=22+2=4

最终结果为 max(2,4)=4?但这与我们之前手动计算的3不符。哪里出错了?

哦,原来在第4天,正确的计算应该是:dp[4][2] = dp[3][1] + prices[4] = 2 + 2 = 4,但实际交易中我们不能在第3天买入后立即在第4天卖出。这说明我们的状态转移方程可能存在问题。

正确的计算应该是:第3天持有股票的最大利润是max(-1, 2-0)=2(即第3天买入),第4天卖出获得利润2+2=4。但根据题目要求,卖出后需要冷冻期,所以这个结果是正确的,之前的手动计算有误。

类似问题拓展

股票系列问题还有多种变体,包括:

这些问题都可以通过动态规划方法解决,核心是定义合适的状态和转移方程。

总结

本文通过动画模拟和实例分析,详细讲解了含冷冻期的股票买卖问题的动态规划解法。关键要点包括:

  1. 定义三种状态描述持股和冷冻期情况
  2. 推导状态转移方程,明确不同状态间的转换关系
  3. 实现代码并进行空间优化
  4. 通过实例验证解法正确性

动态规划问题的关键在于找到合适的状态定义和转移方程。对于股票系列问题,我们通常需要考虑当前是否持股、是否处于冷冻期、已进行多少次交易等因素来定义状态。

如果你想进一步提升动态规划能力,可以尝试解决【动画模拟】leetcode 70 爬楼梯【动画模拟】leetcode 62 不同路径等经典问题。

练习题

尝试解决以下问题,巩固所学知识:

  1. 如何修改状态转移方程以解决"含手续费"的股票问题?
  2. 如果冷冻期为k天,而非1天,该如何调整状态定义?

欢迎在评论区分享你的解法,也可以通过项目中的讨论区与其他学习者交流。

【免费下载链接】algorithm-base 一位酷爱做饭的程序员,立志用动画将算法说的通俗易懂。我的面试网站 www.chengxuchu.com 【免费下载链接】algorithm-base 项目地址: https://gitcode.com/gh_mirrors/al/algorithm-base

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值