原题
动态规划中等1 种解法

#309买卖股票的最佳时机含冷冻期

交易次数不限,但卖出后的下一天不能买入,求最大利润。

#数组#动态规划#状态机#股票

原题

给定一个整数数组prices,其中第  prices[i] 表示第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: prices = [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

示例 2:

输入: prices = [1]
输出: 0

提示:

  • 1 <= prices.length <= 5000
  • 0 <= prices[i] <= 1000

查看原题

解题主线

  1. 每天结束时严格区分休息、持股、当天卖出三种状态;只有前一天处于休息状态,今天才能买入。
  2. 今天的休息状态可来自昨天继续休息或昨天卖出,后者经过今天后才解除冷冻。
  3. 当天卖出必须来自昨天持股,不能从今天刚买入的状态转移。

解法 1:三状态动态规划

维护 rest(不持股且今天未卖出)、hold(持股)和 sold(今天卖出);买入只能从前一天 rest 转移,从而自然落实一天冷冻期。

  • 时间复杂度: O(n)

  • 空间复杂度: O(1)

JAVA
final class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length < 2) {
            return 0;
        }

        int rest = 0;
        int hold = -prices[0];
        // 首日不可能卖出,用负无穷表示该状态不可达。
        int sold = Integer.MIN_VALUE;

        for (int i = 1; i < prices.length; i++) {
            // 保存前一天快照,避免同一天的新状态串联转移。
            int previousRest = rest;
            int previousHold = hold;
            int previousSold = sold;

            // 休息可来自继续休息或昨天卖出后度过冷冻日。
            rest = Math.max(previousRest, previousSold);
            // 买入只能来自昨天的 rest,不能紧接在卖出后的次日。
            hold = Math.max(previousHold, previousRest - prices[i]);
            sold = previousHold + prices[i];
        }
        // 最终持股尚未兑现利润,只比较两个不持股状态。
        return Math.max(rest, sold);
    }
}

实现提示

  • Integer.MIN_VALUE 只用于首日不可达的 sold;后续 sold 始终由可达的 hold 加价格得到,不会发生哨兵加法溢出。

边界与易错点

  • 旧 Q188_maxStockProfit_frozen 实际属于题号 309,且状态注释与转移方程错位;例如 [2, 1] 会错误返回 1,不能直接保留。
  • 休息状态不是“当天处于冷冻期”的同义词:它表示今天没有卖出且不持股,下一天允许买入。
  • 当天卖出状态在第 0 天不可达,应使用负无穷哨兵而不是 0,避免状态语义被放宽。

整理来源

由旧仓库源码复核、去重并整理;展示代码已按 Java 21 语义修正明显问题。

  • leetcode/src/main/java/greedy/Q188_maxStockProfit_frozen.java
  • leetcode/src/main/java/greedy/Q309_maxStockProfit_frozen.java