(LeetCode-Hot100)309. 买卖股票的最佳时机含冷冻期

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

问题简介

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

题解github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

题目描述

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格。

你可以在以下限制条件下完成尽可能多的交易(多次买卖一支股票):

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票(即存在 1 天的冷冻期)。

目标:计算你能获得的最大利润。


📌 示例说明

示例 1:

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

示例 2:

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

💡 解题思路

本题是经典的「股票买卖」系列问题之一,加入了「冷冻期」这一新约束。核心在于 状态机建模动态规划

方法一:状态机动态规划(推荐)

步骤 1:定义状态

每天结束时,我们可能处于以下三种状态之一:

  • hold:持有股票(今天买入或之前买入未卖出)
  • sold:今天刚卖出股票(明天进入冷冻期)
  • rest:不持有股票,且不在冷冻期(可以买入)

注意:rest 包括两种情况:从未买过,或冷冻期已结束。

步骤 2:状态转移方程

  • hold[i] = max(hold[i-1], rest[i-1] - prices[i])
    → 要么继续持有,要么从 rest 状态买入

  • sold[i] = hold[i-1] + prices[i]
    → 只能从持有状态卖出

  • rest[i] = max(rest[i-1], sold[i-1])
    → 要么继续保持空仓,要么从昨天的 sold(冷冻期结束)转来

步骤 3:初始化

  • 第 0 天:
    • hold = -prices[0](买入)
    • sold = 0(无法卖出)
    • rest = 0(不操作)

步骤 4:最终答案

最后一天,最大利润为 max(sold, rest)(不可能持有股票,因为持有不如卖出)


方法二:空间优化(滚动变量)

由于每天只依赖前一天的状态,可用三个变量代替数组,将空间复杂度降至 O(1)。


方法三:另一种状态定义(可选)

也可以定义 dp[i][0/1] 表示第 i 天是否持有股票,但需额外处理冷冻期逻辑(如 dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]),而 dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])),因为买入只能在非冷冻期,即至少两天前卖出。

这种方法也可行,但状态机更清晰。


💻 代码实现

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) return 0;
        
        int hold = -prices[0]; // 持有股票
        int sold = 0;          // 今天卖出
        int rest = 0;          // 空仓且非冷冻
        
        for (int i = 1; i < prices.length; i++) {
            int prevHold = hold;
            int prevSold = sold;
            int prevRest = rest;
            
            hold = Math.max(prevHold, prevRest - prices[i]);
            sold = prevHold + prices[i];
            rest = Math.max(prevRest, prevSold);
        }
        
        return Math.max(sold, rest);
    }
}
func maxProfit(prices []int) int {
    if len(prices) == 0 {
        return 0
    }
    
    hold := -prices[0] // 持有股票
    sold := 0          // 今天卖出
    rest := 0          // 空仓且非冷冻
    
    for i := 1; i < len(prices); i++ {
        prevHold, prevSold, prevRest := hold, sold, rest
        
        hold = max(prevHold, prevRest - prices[i])
        sold = prevHold + prices[i]
        rest = max(prevRest, prevSold)
    }
    
    return max(sold, rest)
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

🧪 示例演示

prices = [1,2,3,0,2] 为例:

DayPriceholdsoldrest
01-100
12max(-1, 0-2)=-1-1+2=1max(0,0)=0
23max(-1,0-3)=-1-1+3=2max(0,1)=1
30max(-1,1-0)=1-1+0=-1max(1,2)=2
42max(1,2-2)=11+2=3max(2,-1)=2

最终 max(sold=3, rest=2) = 3


✅ 答案有效性证明

  • 状态覆盖完整:三种状态互斥且穷尽所有可能。
  • 转移正确
    • 买入只能从 rest(非冷冻)出发;
    • 卖出后必进入 sold,次日自动变为 rest 的候选;
    • rest 可由自身延续或从 sold 转入(冷冻期结束)。
  • 边界处理正确:第 0 天初始化合理。
  • 最优子结构:每一天的最优解由前一天的最优解推导而来。

因此,算法正确。


📊 复杂度分析

项目复杂度
时间复杂度O(n)
空间复杂度O(1)

仅使用常数个变量,遍历一次数组。


📌 问题总结

  • 关键点:引入「冷冻期」后,不能简单用 buy/sell 两状态,需增加 rest 状态区分是否可买入。
  • 技巧:状态机建模是处理带约束 DP 问题的强大工具。
  • 扩展:类似问题如「含手续费」「最多 k 次交易」等,均可通过扩展状态解决。
  • 记忆口诀

    “持、卖、歇,三态流转;
    买从歇起,卖入持中;
    冷冻一日,歇承卖终。”

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值