LeetCode-Go中的动态规划多状态问题:从股票买卖到打家劫舍

LeetCode-Go中的动态规划多状态问题:从股票买卖到打家劫舍

【免费下载链接】LeetCode-Go 该内容是使用Go语言编写的LeetCode题目的完整解决方案集合,实现了100%的测试覆盖率,并且运行时间优于所有题目100%的提交结果。 【免费下载链接】LeetCode-Go 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

动态规划(Dynamic Programming, DP)是解决多阶段决策问题的高效方法,尤其在处理包含多种状态转换的场景时表现突出。本文将以LeetCode-Go项目中的经典问题为例,深入解析如何通过状态定义与转移方程设计,解决股票买卖和打家劫舍等多状态DP问题。这些解决方案均来自LeetCode-Go项目源码,该项目实现了100%测试覆盖率且运行效率优于99%的提交记录。

动态规划多状态问题的核心框架

多状态DP问题的关键在于定义清晰的状态变量构建无后效性的转移方程。以股票买卖问题为例,我们需要跟踪"持有股票"和"不持有股票"两种核心状态,并考虑冷冻期、交易次数限制等约束条件。项目中采用的通用框架如下:

  1. 状态定义dp[i][s]表示第i天处于状态s时的最大收益(s=0表示不持有,s=1表示持有)
  2. 转移方程:通过前一天的状态推导当前状态
  3. 空间优化:使用滚动数组将二维DP压缩为常数空间

项目结构

股票买卖问题的状态演进

基础版:单次交易(Best Time to Buy and Sell Stock)

[121. Best Time to Buy and Sell Stock](https://link.gitcode.com/i/395fcd9f4bd0c9eecea9e397be6e5c02/blob/25c03cf13afa6ffb2bb305940c9bced152214536/leetcode/0121.Best-Time-to-Buy-and-Sell-Stock/121. Best Time to Buy and Sell Stock.go?utm_source=gitcode_repo_files) 要求只能完成一笔交易,本质是寻找价格序列中的最大差值。项目中采用贪心+DP的混合解法:

// 解法一 模拟DP
func maxProfit(prices []int) int {
    if len(prices) < 1 {
        return 0
    }
    minPrice, maxProfit := prices[0], 0  // 状态变量:最低买入价和最大利润
    for i := 1; i < len(prices); i++ {
        if prices[i]-minPrice > maxProfit {
            maxProfit = prices[i] - minPrice  // 更新最大利润(不持有状态)
        }
        if prices[i] < minPrice {
            minPrice = prices[i]  // 更新最低买入价(持有状态)
        }
    }
    return maxProfit
}

该实现通过两个变量模拟了持有/不持有的状态转换,将空间复杂度优化至O(1),时间效率超越100%提交。

进阶版:含冷冻期的多次交易

当允许多次买卖但加入"卖出后次日不能买入"的冷冻期约束时(如[309. Best Time to Buy and Sell Stock with Cooldown]),需要引入三维状态:

  • dp[i][0]:不持有股票且不在冷冻期
  • dp[i][1]:持有股票
  • dp[i][2]:不持有股票且在冷冻期

状态转移关系如下:

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]

打家劫舍系列的状态迁移

线性房屋:相邻约束问题

[198. House Robber](https://link.gitcode.com/i/395fcd9f4bd0c9eecea9e397be6e5c02/blob/25c03cf13afa6ffb2bb305940c9bced152214536/leetcode/0198.House-Robber/198. House Robber.go?utm_source=gitcode_repo_files) 是典型的相邻状态互斥问题,项目中定义了两种状态:

  • dp[i]:抢劫前i间房屋的最大金额
  • 转移方程:dp[i] = max(dp[i-1], nums[i]+dp[i-2])

核心实现如下:

func rob(nums []int) int {
    n := len(nums)
    if n == 0 {
        return 0
    }
    if n == 1 {
        return nums[0]
    }
    // dp[i] 代表抢 nums[0...i] 房子的最大价值
    dp := make([]int, n)
    dp[0], dp[1] = nums[0], max(nums[1], nums[0])
    for i := 2; i < n; i++ {
        dp[i] = max(dp[i-1], nums[i]+dp[i-2])  // 不抢i或抢i(则i-1不能抢)
    }
    return dp[n-1]
}

环形房屋:首尾约束问题

当房屋呈环形排列时([213. House Robber II]),需要拆分两种场景:

  1. 不抢劫第一间房,问题退化为线性排列的nums[1:]
  2. 不抢劫最后一间房,问题退化为线性排列的nums[:n-1]

最终结果取两种场景的最大值,项目中通过调用基础版rob函数实现:

func robII(nums []int) int {
    if len(nums) == 1 {
        return nums[0]
    }
    return max(rob(nums[1:]), rob(nums[:len(nums)-1]))
}

多状态DP问题的通用解题策略

通过分析股票买卖和打家劫舍系列问题,可以提炼出多状态DP的解题模板:

1. 状态定义三要素

  • 时间维度:通常为问题中的阶段(天数、房屋序号等)
  • 状态变量:描述当前决策的核心属性(持有/不持有、是否抢劫等)
  • 目标值:需要优化的目标函数(利润、金额等)

2. 状态转移四步法

  1. 枚举所有可能状态:列出每个阶段的所有可能状态
  2. 确定转移条件:分析状态间的转换规则(如"买入"需从"不持有"状态转换)
  3. 构建转移方程:用数学公式表达状态间的数值关系
  4. 处理边界条件:初始化起始状态和特殊情况

3. 空间优化技巧

  • 滚动数组:当转移只依赖前1-2个状态时,用变量替代数组(如股票问题)
  • 状态合并:将互斥状态合并为同一维度的不同取值(如打家劫舍的0/1状态)

实战应用:从源码到解题

LeetCode-Go项目中对多状态DP问题的处理展现了极高的工程技巧。以[0309. Best Time to Buy and Sell Stock with Cooldown]为例,项目代码通过以下方式确保效率:

  1. 状态压缩:将三维DP压缩为三个变量
func maxProfit(prices []int) int {
    if len(prices) == 0 {
        return 0
    }
    dp0, dp1, dp2 := 0, -prices[0], 0  // 初始状态
    for i := 1; i < len(prices); i++ {
        newDp0 := max(dp0, dp2)
        newDp1 := max(dp1, dp0 - prices[i])
        newDp2 := dp1 + prices[i]
        dp0, dp1, dp2 = newDp0, newDp1, newDp2
    }
    return max(dp0, dp2)
}
  1. 枚举优化:通过单调栈处理价格波动(如[122. Best Time to Buy and Sell Stock II]的解法二)

总结与扩展

多状态动态规划通过引入额外的状态维度,有效解决了包含约束条件的决策问题。LeetCode-Go项目中的实现展示了三个关键优化方向:

  • 状态精简:只保留影响决策的核心状态变量
  • 空间压缩:利用滚动数组将O(n)空间降至O(1)
  • 边界处理:通过预处理规避数组越界等异常情况

这些技巧不仅适用于股票和抢劫问题,还可推广到背包问题、图论路径规划等领域。建议结合项目中的动态规划专题源码进行系统学习,重点关注状态定义与转移方程的设计思路。

提示:更多多状态DP问题的解决方案可查看项目中的dp标签目录,包含从基础到进阶的完整实现。

【免费下载链接】LeetCode-Go 该内容是使用Go语言编写的LeetCode题目的完整解决方案集合,实现了100%的测试覆盖率,并且运行时间优于所有题目100%的提交结果。 【免费下载链接】LeetCode-Go 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值