2026/11/5 4:16:19

动态规划解决爬楼梯最小花费问题

动态规划解决爬楼梯最小花费问题 1. 问题理解与场景拆解这道LeetCode题目描述了一个经典的动态规划应用场景假设你正在爬楼梯每次可以选择爬1阶或2阶。每个台阶都有一个对应的花费值当你踩上某个台阶时就需要支付对应的费用。题目要求计算爬到楼梯顶部的最小总花费。实际工程中类似的场景比比皆是比如游戏角色在不同路径中选择消耗最少金币的路线、物流配送中寻找成本最低的运输路径、甚至是生活中选择最省钱的购物方案。这类问题的核心都是在多阶段决策过程中寻找全局最优解。题目给出的示例非常直观输入cost [10,15,20]输出15 解释为从下标为1的台阶开始支付15向上爬两阶到达顶部总花费15。2. 动态规划解法详解2.1 状态定义与转移方程我们定义dp[i]为到达第i个台阶所需的最小花费。这里有个关键点需要注意题目描述的顶部实际上是第n个台阶之后的位置ncost.length。因此我们需要计算的是dp[n]而不是dp[n-1]。状态转移方程推导如下要到达第i阶只有两种可能从i-1阶跨1步上来总花费dp[i-1]cost[i-1]从i-2阶跨2步上来总花费dp[i-2]cost[i-2]取两者较小值即为dp[i]因此状态转移方程为 dp[i] min(dp[i-1] cost[i-1], dp[i-2] cost[i-2])2.2 边界条件处理初始状态需要特别注意dp[0] 0 地面是起点不需要花费dp[1] 0 可以直接跨到第1阶不经过任何台阶这里容易犯的错误是将dp[1]初始化为cost[0]。实际上根据题意站在台阶上才需要支付费用而选择从哪个台阶开始是不需要支付费用的。2.3 空间优化技巧观察状态转移方程可以发现dp[i]只依赖于前两个状态dp[i-1]和dp[i-2]。因此我们可以用两个变量代替整个dp数组将空间复杂度从O(n)优化到O(1)。实现时使用prev和curr两个变量prev表示dp[i-2]curr表示dp[i-1]每次迭代计算next min(curr cost[i-1], prev cost[i-2])然后更新prev curr, curr next3. 完整代码实现与逐行解析def minCostClimbingStairs(cost: List[int]) - int: n len(cost) if n 1: return cost[0] # 初始化前两个状态 prev, curr 0, 0 for i in range(2, n 1): # 计算下一步的最小花费 next_step min(curr cost[i-1], prev cost[i-2]) # 滚动更新状态 prev, curr curr, next_step return curr代码解析首先处理边界情况当只有1个台阶时直接选择从地面开始跨到顶部不踩任何台阶初始化prev和curr为0对应dp[0]和dp[1]从i2开始迭代到in包含n每次计算下一步的最小花费考虑从i-1跨1步或从i-2跨2步滚动更新状态变量最终curr即为dp[n]即到达顶部的最小花费4. 复杂度分析与变种讨论4.1 时间复杂度分析原始DP解法O(n)需要遍历整个cost数组一次空间优化版同样O(n)时间但空间降为O(1)4.2 相关变种问题路径记录如果需要输出具体路径而不仅是花费可以额外维护一个数组记录每个状态的选择三步爬楼如果每次可以爬1、2或3阶状态转移方程扩展为三项取最小花费定义变化如果花费是在离开台阶时支付而非踏上时支付需要调整状态转移逻辑带障碍版本某些台阶不可达花费为无穷大需要在状态转移时加入判断5. 常见错误与调试技巧5.1 典型错误案例边界条件错误错误地将dp[1]初始化为cost[0]忘记处理n1的特殊情况索引越界在循环中错误地访问cost[-1]没有正确处理i-2可能为负的情况状态转移混淆混淆踏上台阶和跨越台阶的花费计算时机错误地将dp[n]当作最终结果实际应为dp[n1]5.2 调试建议先用小例子手动计算dp数组验证思路打印中间状态变量观察其变化是否符合预期特别注意循环的起始和终止条件对于空间优化版本可以先用标准DP实现再逐步优化6. 实际工程应用扩展虽然这是一个算法练习题但其核心思想在工程中有广泛应用资源调度优化在有限资源下选择最优任务执行路径游戏AI决策NPC寻找消耗最低的行动路线金融投资策略在多阶段投资中选择成本最低的方案网络路由选择寻找延迟最小的数据传输路径理解这类问题的关键在于准确识别阶段台阶明确定义状态到达某个阶段的花费正确建立状态转移关系选择策略在实际编码面试中面试官可能会要求解释状态转移方程的含义或者讨论如何修改条件来适应不同的场景。因此不仅要会写代码更要理解每个决策背后的逻辑。