状态必须拆成三个而非两个,因冷冻期会打断交易连续性:卖完次日不能买,但可持有或空仓;两状态无法区分“刚卖出”与“已空仓多天”,前者次日须冷冻,后者可买入;常见三状态为dpi(持有)、dpi(卖出当天)、dpi(空仓且非冷冻),冷冻期仅为sold的后效,无需独立状态。

状态定义为什么必须拆成三个而不是两个
因为冷冻期会打断交易连续性:卖完股票后下一天不能买,但可以继续持有或空仓。如果只用“持有/不持有”两个状态,就无法区分“今天刚卖出”和“已经空仓多天”这两种情况——前者明天必须冷冻,后者明天可以买入。dp[i][0](持有)、dp[i][1](卖出当天)、dp[i][2](空仓且非冷冻)是常见拆分;也有写成 hold、sold、rest 的,本质一样。
容易踩的坑:有人把“冷冻期”单独设为第四个状态,反而让转移变复杂。其实冷冻期不是独立状态,而是 sold 的**后效**——它只影响下一轮能否买入,所以直接由 sold 转移到 rest 即可,无需额外状态。
状态转移方程怎么推导才不绕晕
从第 i-1 天出发,看每个状态能怎么走到第 i 天:
-
dp[i][0](第 i 天持有):要么前一天就持有并没动,要么前一天是rest状态(即非冷冻空仓),今天买入 →dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i]) -
dp[i][1](第 i 天卖出):只能从前一天持有状态来 →dp[i][1] = dp[i-1][0] + prices[i] -
dp[i][2](第 i 天空仓且非冷冻):要么前一天就是rest,要么前一天是sold(今天自然解除冷冻)→dp[i][2] = max(dp[i-1][2], dp[i-1][1])
注意:dp[i-1][1] 转到 dp[i][2] 就是冷冻期结束的体现——没有“冷冻状态”,只有“昨天卖了,今天自动变成可交易空仓”。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
空间优化时为什么只能滚动三个变量
每个状态只依赖前一时刻的三个值,所以可以用六个变量滚动(如 hold_pre, sold_pre, rest_pre 和对应当前值),但必须保证更新顺序不污染:
- 先算新
sold(依赖旧hold) - 再算新
rest(依赖旧sold和旧rest) - 最后算新
hold(依赖旧rest,不能用刚更新的rest)
错误写法:rest = max(rest, sold); hold = max(hold, rest - price); ——这里用了刚更新的 rest,逻辑就错了。正确顺序是 sold → rest → hold,且所有右侧都用上一轮的值。
边界初始化为什么 dp[0][1] 和 dp[0][2] 不能设为 0
第 0 天不可能卖出(没东西可卖),所以 dp[0][1] = -INF(或一个极小值,如 INT_MIN/2,避免溢出);dp[0][2] 是空仓非冷冻,合法,值为 0;dp[0][0] 是买入,值为 -prices[0]。
如果把 dp[0][1] 初始化为 0,后续计算中它会错误地参与 max,导致提前“假装卖过”,结果偏高。这也是调试时常见 bug 来源——输出中间状态,看第 0 天的 sold 是否异常地参与了转移。
实际编码中,用 INT_MIN/2 比 INT_MIN 更安全,因为之后可能加减价格,防止整数溢出。

















