news 2026/10/31 5:11:03

动态规划解股票买卖问题:状态转移与空间优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态规划解股票买卖问题:状态转移与空间优化

1. 动态规划解股票问题的核心思路

股票买卖问题作为动态规划经典题型,其核心在于状态定义和状态转移。面对含冷冻期、手续费等复杂条件时,我们需要更精细地划分状态。以309题为例,我将状态划分为四类:

  • 状态0:持有股票(可能是今天买入或之前持有)
  • 状态1:不持有股票且非冷冻期(保持卖出状态)
  • 状态2:今天卖出股票
  • 状态3:冷冻期(仅持续一天)

这种划分方式比传统的三状态更清晰,能避免状态混淆。关键点在于区分"今天卖出"和"保持卖出"两种不持股状态,因为冷冻期只与卖出操作直接相关。

2. 309题含冷冻期的状态转移详解

2.1 状态转移方程推导

对于每个状态,转移逻辑如下:

  1. 持有股票(状态0):

    • 保持持有:dp[i-1][0]
    • 今天买入:从冷冻期(dp[i-1][3])或保持卖出状态(dp[i-1][1])转移
    • 转移方程:max(前日持有, 前日冷冻期买入, 前日保持卖出买入)
  2. 保持卖出(状态1):

    • 来源:前一日已经是状态1或前一日是冷冻期(状态3)
    • 转移方程:max(dp[i-1][1], dp[i-1][3])
  3. 今天卖出(状态2):

    • 唯一来源:前一日持有股票今日卖出
    • 转移方程:dp[i-1][0] + prices[i]
  4. 冷冻期(状态3):

    • 唯一来源:前一日卖出股票
    • 转移方程:dp[i-1][2]

2.2 初始化技巧

第0天初始化需要特别注意:

  • dp[0][0] = -prices[0](首日买入)
  • dp[0][1] = 0(理论上不可能,但递推需要)
  • dp[0][2] = 0(未发生卖出)
  • dp[0][3] = 0(未发生冷冻期)

这种初始化保证了递推的正确性,特别是对于i=1时的特殊情况处理。

3. 空间优化实战方案

3.1 二维数组标准解法

def maxProfit(prices): n = len(prices) if n < 2: return 0 dp = [[0]*4 for _ in range(n)] dp[0][0] = -prices[0] for i in range(1, n): dp[i][0] = max(dp[i-1][0], max(dp[i-1][1], dp[i-1][3]) - prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][3]) dp[i][2] = dp[i-1][0] + prices[i] dp[i][3] = dp[i-1][2] return max(dp[-1][1], dp[-1][2], dp[-1][3])

3.2 一维数组优化

我们发现dp[i]只依赖dp[i-1],因此可以压缩空间:

def maxProfit(prices): if not prices: return 0 dp0, dp1, dp2, dp3 = -prices[0], 0, 0, 0 for price in prices[1:]: new0 = max(dp0, max(dp1, dp3) - price) new1 = max(dp1, dp3) new2 = dp0 + price new3 = dp2 dp0, dp1, dp2, dp3 = new0, new1, new2, new3 return max(dp1, dp2, dp3)

优化后空间复杂度从O(n)降到O(1),这在处理大数据量时优势明显。

4. 714题含手续费的处理技巧

4.1 手续费处理时机选择

手续费可以在买入或卖出时扣除,但建议统一在卖出时处理,因为:

  • 更符合实际交易场景
  • 避免买入时资金不足的边界问题
  • 代码实现更统一

状态转移只需修改卖出方程:

dp[i][2] = dp[i-1][0] + prices[i] - fee

4.2 完整代码实现

def maxProfit(prices, fee): n = len(prices) if n < 2: return 0 dp = [[0]*4 for _ in range(n)] dp[0][0] = -prices[0] for i in range(1, n): dp[i][0] = max(dp[i-1][0], max(dp[i-1][1], dp[i-1][3]) - prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][3]) dp[i][2] = dp[i-1][0] + prices[i] - fee dp[i][3] = dp[i-1][2] return max(dp[-1][1], dp[-1][2], dp[-1][3])

5. 188题K次交易限制解法

5.1 三维DP数组定义

对于最多K次交易的问题,需要增加交易次数的维度:

  • dp[i][k][0]:第i天,已完成k次交易,不持有股票
  • dp[i][k][1]:第i天,已完成k次交易,持有股票

5.2 状态转移方程

for i in range(1, n): for k in range(1, K+1): dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])

5.3 空间优化技巧

使用二维数组滚动更新:

dp = [[0]*2 for _ in range(K+1)] for i in range(n): for k in range(K, 0, -1): dp[k][0] = max(dp[k][0], dp[k][1] + prices[i]) dp[k][1] = max(dp[k][1], dp[k-1][0] - prices[i])

6. 股票问题通用解题框架

6.1 状态定义方法论

  1. 首先确定基本状态:持有/不持有
  2. 根据附加条件增加状态维度:
    • 冷冻期:增加卖出状态
    • 交易次数:增加k维度
    • 手续费:在卖出时扣除

6.2 调试技巧

建议打印DP表验证:

def print_dp(dp): print("天数\t持有\t保持卖出\t今日卖出\t冷冻期") for i in range(len(dp)): print(f"{i}\t{dp[i][0]}\t{dp[i][1]}\t\t{dp[i][2]}\t\t{dp[i][3]}")

6.3 常见错误排查

  1. 初始化错误:确保dp[0][0]正确初始化为-prices[0]
  2. 状态混淆:特别注意冷冻期只能由卖出状态转移而来
  3. 边界条件:处理prices为空或长度为1的情况
  4. 最终结果:需要比较多个不持有股票状态的最大值

7. 不同语言实现要点

7.1 Java实现注意点

// 使用2*4数组优化空间 int[][] dp = new int[2][4]; dp[0][0] = -prices[0]; for(int i = 1; i < prices.length; i++){ dp[i%2][0] = Math.max(dp[(i-1)%2][0], Math.max(dp[(i-1)%2][1], dp[(i-1)%2][3]) - prices[i]); // 其他状态转移... }

7.2 C++实现技巧

// 使用vector预分配内存提高性能 vector<vector<int>> dp(n, vector<int>(4)); dp[0][0] = -prices[0];

7.3 Go语言特性利用

// 使用切片复用内存 dp := make([][4]int, n) dp[0][0] = -prices[0]

8. 复杂度分析与优化对比

8.1 时间复杂度

所有解法时间复杂度均为O(n),区别仅在于:

  • 基本解法:O(n)空间
  • 优化解法:O(1)空间

8.2 实际性能测试

在LeetCode测试中:

  • Python优化版:运行时间减少30%
  • Java二维数组版:内存消耗降低50%
  • Go语言版:综合性能最优

9. 扩展思考:状态机模型

将股票问题视为状态机:

  • 每个状态是一个节点
  • 状态转移是边
  • 边权重是收益变化

这种模型化思维可以帮助理解更复杂的问题变种,如:

  • 加入交易延迟
  • 考虑成交量限制
  • 多资产组合交易

10. 实战训练建议

  1. 建议刷题顺序:

    • 121(基础)→122(无限次)→309(冷冻期)→714(手续费)→123/188(次数限制)
  2. 调试方法:

    • 先在小样例(如[1,2,3,0,2])上手动计算DP表
    • 对比程序输出排查错误
  3. 进阶挑战:

    • 尝试统一模板解决所有股票问题
    • 思考如何加入更多现实约束条件
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 5:09:22

大空间吊顶空调选型与ZX2机组核心技术解析

1. 大空间吊顶空调选型要点解析在商业建筑、厂房、体育馆等大空间场所&#xff0c;传统的壁挂式或柜式空调往往难以满足均匀送风的需求。吊顶式空调射流机组凭借其独特的气流组织方式&#xff0c;成为这类场所的首选方案。ZX2型吊顶式空调射流机组作为行业主流产品&#xff0c;…

作者头像 李华
网站建设 2026/10/31 5:08:17

把技能当项目管:刻意练习、量化评估与技能树的复利系统

说实话&#xff0c;最近几年我越来越烦“skills”这个词被滥用。简历上写“熟练使用Python”的人&#xff0c;可能连一份完整的数据清洗文档都写不出来&#xff1b;朋友圈到处转发“终身学习”&#xff0c;但真正把一项能力练到能交付的人寥寥无几。原因不是大家不努力&#xf…

作者头像 李华
网站建设 2026/10/31 5:08:02

技能习得的底层逻辑:从知道到做到,掌握刻意练习的实战框架

我见过太多人把“学技能”活成了“收藏技能”&#xff1a;网盘里躺着几十个教程&#xff0c;收藏夹里堆满了干货文章&#xff0c;付费课程买了好几门&#xff0c;打卡群加了七八个&#xff0c;三个月后回头一看&#xff0c;好像什么都会一点&#xff0c;又什么都不会。说真的&a…

作者头像 李华
网站建设 2026/10/31 5:07:31

Dots 500美元订阅方案拆解:聊天免费、干活按量计费的定价逻辑

Dots上线了新的订阅方案&#xff0c;每月500美元&#xff0c;聊天不扣额度&#xff0c;干活照旧扣。这句话我最近在好几个技术社群里都看到有人转发&#xff0c;讨论度比想象中高。关注点其实不在500美元这个价格本身——比这贵的AI产品多了去了——而在于它把“聊天”和“干活…

作者头像 李华
网站建设 2026/10/31 5:06:12

贝叶斯方法解决零失效数据可靠性评估难题

1. 无失效数据场景下的可靠性评估困境在产品质量管理领域&#xff0c;我们常常遇到一个令人头疼的现状&#xff1a;某些高价值产品在测试周期内可能完全没有出现失效案例。比如航空航天部件、医疗植入设备或者高端工业设备&#xff0c;由于产品本身的高可靠性设计和严格的品控流…

作者头像 李华
网站建设 2026/10/31 5:06:03

期望搜索原理与实现:爱因斯坦棋博弈AI开发实战

简介&#xff1a;基于期望搜索算法构建的爱因斯坦棋博弈软件&#xff0c;面向计算机博弈大赛参赛者、棋类爱好者及人工智能算法学习者。压缩包共159个文件&#xff0c;约7.38MB&#xff0c;内含Python源码、png界面素材、sample对局样例、xml配置参数以及ttf字体等&#xff1b;…

作者头像 李华