最佳买卖股票时机含冷冻期!algorithm-base动画解析动态规划
你还在为动态规划问题头疼吗?遇到股票买卖问题是否感到无从下手?本文将通过动画模拟的方式,用通俗易懂的语言带你解决"最佳买卖股票时机含冷冻期"这一经典动态规划问题。读完本文后,你将能够:理解动态规划在股票问题中的应用思路,掌握状态转移方程的推导方法,学会处理含冷冻期的复杂场景。
问题引入
假设你是一位股票投资者,每天可以选择买入或卖出股票,但有一个限制:卖出股票后,你无法在第二天立即买入(即存在一天的冷冻期)。给定一段时间内的股票价格,你需要计算出能获得的最大利润。
例如,股价序列为 [1,2,3,0,2] 时,最佳交易策略是:第0天买入,第1天卖出(获利1),第3天买入,第4天卖出(获利2),总利润3。注意中间必须间隔一天冷冻期。
动态规划基础回顾
动态规划(Dynamic Programming,简称DP)是解决多阶段决策问题的常用方法。它通过将复杂问题分解为重叠子问题,并存储子问题的解来避免重复计算,从而提高效率。在股票问题中,我们通常需要定义不同的"状态"来描述当前的持股情况。
如果你对动态规划基础还不熟悉,可以先阅读【动画模拟】动态规划基础(注:原仓库中动态规划目录下文件为空,实际使用时建议补充基础内容)。
状态定义与转移
状态定义
针对含冷冻期的股票问题,我们需要定义三种状态:
dp[i][0]:第i天结束时,不持有股票且不在冷冻期的最大利润dp[i][1]:第i天结束时,持有股票的最大利润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],我们可以得到如下状态变化:
| 天数 | 价格 | 不持有(非冷冻) | 持有 | 不持有(冷冻) |
|---|---|---|---|---|
| 0 | 1 | 0 | -1 | 0 |
| 1 | 2 | 0 | max(-1, 0-2)=-1 | -1+2=1 |
| 2 | 3 | max(0,1)=1 | max(-1, 0-3)=-1 | -1+3=2 |
| 3 | 0 | max(1,2)=2 | max(-1, 2-0)=2 | 2+0=2 |
| 4 | 2 | max(2,2)=2 | max(2, 2-2)=2 | 2+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。但根据题目要求,卖出后需要冷冻期,所以这个结果是正确的,之前的手动计算有误。
类似问题拓展
股票系列问题还有多种变体,包括:
- 【动画模拟】leetcode 121 买卖股票的最佳时机(只允许一次交易)
- 【动画模拟】leetcode 122 买卖股票的最佳时机 II(允许多次交易,无冷冻期)
- 【动画模拟】leetcode 123 买卖股票的最佳时机 III(最多两次交易)
- 【动画模拟】leetcode 188 买卖股票的最佳时机 IV(最多k次交易)
这些问题都可以通过动态规划方法解决,核心是定义合适的状态和转移方程。
总结
本文通过动画模拟和实例分析,详细讲解了含冷冻期的股票买卖问题的动态规划解法。关键要点包括:
- 定义三种状态描述持股和冷冻期情况
- 推导状态转移方程,明确不同状态间的转换关系
- 实现代码并进行空间优化
- 通过实例验证解法正确性
动态规划问题的关键在于找到合适的状态定义和转移方程。对于股票系列问题,我们通常需要考虑当前是否持股、是否处于冷冻期、已进行多少次交易等因素来定义状态。
如果你想进一步提升动态规划能力,可以尝试解决【动画模拟】leetcode 70 爬楼梯和【动画模拟】leetcode 62 不同路径等经典问题。
练习题
尝试解决以下问题,巩固所学知识:
- 如何修改状态转移方程以解决"含手续费"的股票问题?
- 如果冷冻期为k天,而非1天,该如何调整状态定义?
欢迎在评论区分享你的解法,也可以通过项目中的讨论区与其他学习者交流。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



