题目描述
思路解析
一句话答案:LeetCode 746 使用最小花费爬楼梯用一维 DP:dp[i] 记到 i 阶最小花费,min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])。楼顶不收费,答案 dp[n],时间 O(n)、空间 O(1)。
这道题到底在求什么
给一个数组 cost,下标 i 处的值是「离开第 i 阶」要付的钱。你可以从第 0 阶或第 1 阶起步(起步不花钱),每次向上走 1 阶或 2 阶,目标是花最少的钱爬到楼顶。这里有个最容易踩的坑:楼顶不收费。cost 只到最后一阶,站上楼顶不再「离开」任何台阶,所以不计费,答案是 dp[n] 而非 dp[n-1]。
为什么贪心每步挑便宜的会错
一个很自然的想法是贪心:每步都跳到眼前更便宜的那阶。但眼前省下的这一步,往往把你顶到后面只剩高价阶可踩。以 cost = [1,100,1,1,1,100,1,1,100,1] 为例,那几个 100 就是专门埋来惩罚短视选择的陷阱。
把所有路径枚举一遍当然不会错,但每步两个分支一路分叉,路径数随阶数指数膨胀,根本算不完。出路是记住「到每一阶的最小花费」,让后面的决策直接复用,而不是每次从头再拼。
dp 怎么定义,起步为什么是 0
定义 dp[i] 为「爬到第 i 阶所需的最小花费」。起点铺两个:dp[0] = 0、dp[1] = 0,因为题目允许从第 0 或第 1 阶零成本起步。要分清两件事——踏上某阶不花钱,离开某阶才按 cost 付费。所以「到第 i 阶」的花费只算一路上离开过的台阶费用,落脚的第 i 阶还没离开,不计。
转移为什么是两条来路取 min
站在第 i 阶回头看,上一步只可能来自两处:从第 i-1 阶走 1 阶上来,代价是「到 i-1 阶的最小花费」再加上离开它的 cost[i-1];或者从第 i-2 阶跨 2 阶上来,代价是「到 i-2 阶的最小花费」加 cost[i-2]。两条来路各自的最优都是子问题已经算好的,到第 i 阶的最优就是两者取更小:dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])。
拿题目样例把表填出来
对着 cost = [1,100,1,1,1,100,1,1,100,1] 亲手填一遍最有感觉。起步 dp[0]=0、dp[1]=0。到第 2 阶:从第 1 阶来花 0+100=100,从第 0 阶跳 2 阶来花 0+1=1,取小的 dp[2]=1——第一个 100 就这样被绕开。接着 dp[3]=min(1+1, 0+100)=2,同法逐格上推,到 dp[9]=min(4+100, 4+1)=5、dp[10]=min(5+1, 4+100)=6,楼顶花费 6,正是答案。
复杂度与三个翻车点
从第 2 阶线性推到第 n 阶,每格只做一次比较,时间 O(n);dp[i] 只依赖前两项,用两个变量滚动即可,空间 O(1)。
三个高频错处:一是给楼顶也付了费,可楼顶根本不收费,答案是 dp[n];二是起步费设错,dp[0] 和 dp[1] 都该是 0;三是付错台阶——走到第 i 阶付的是「来路那一阶」的 cost(cost[i-1] 或 cost[i-2]),不是 cost[i]。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住这条转移式,下面每一格都在套它。
上行是各阶离开费 cost(固定不动),下行 dp 待填。从最左边开始。
可以从第 0 阶起步,起步不花钱:dp[0]=0。
也可以从第 1 阶起步:dp[1]=0。两个起点都 0 花费。
算 dp[2]:两条来路——从 1 阶走 1 步花 0+100=100,从 0 阶跳 2 步花 0+1=1。
取较小的 1 填进 dp[2]。跳 2 步更省。
算 dp[3]:两条来路——从 2 阶走 1 步花 1+1=2,从 1 阶跳 2 步花 0+100=100。
取较小的 2 填进 dp[3]。走 1 步更省。
算 dp[4]:两条来路——从 3 阶走 1 步花 2+1=3,从 2 阶跳 2 步花 1+1=2。
取较小的 2 填进 dp[4]。跳 2 步更省。
算 dp[5]:两条来路——从 4 阶走 1 步花 2+1=3,从 3 阶跳 2 步花 2+1=3。
取较小的 3 填进 dp[5]。两条一样。
算 dp[6]:两条来路——从 5 阶走 1 步花 3+100=103,从 4 阶跳 2 步花 2+1=3。
取较小的 3 填进 dp[6]。跳 2 步更省。
算 dp[7]:两条来路——从 6 阶走 1 步花 3+1=4,从 5 阶跳 2 步花 3+100=103。
取较小的 4 填进 dp[7]。走 1 步更省。
算 dp[8]:两条来路——从 7 阶走 1 步花 4+1=5,从 6 阶跳 2 步花 3+1=4。
取较小的 4 填进 dp[8]。跳 2 步更省。
算 dp[9]:两条来路——从 8 阶走 1 步花 4+100=104,从 7 阶跳 2 步花 4+1=5。
取较小的 5 填进 dp[9]。跳 2 步更省。
算 dp[10]:两条来路——从 9 阶走 1 步花 5+1=6,从 8 阶跳 2 步花 4+100=104。
取较小的 6 填进 dp[10]。走 1 步更省。
最右 dp[10]=6 就是爬到楼顶的最小花费。注意楼顶本身不收费。
边界先想清。
两个高频追问。
参考代码
def minCostClimbingStairs(cost): n = len(cost) a, b = 0, 0 # dp[i-2], dp[i-1] for i in range(2, n + 1): a, b = b, min(b + cost[i-1], a + cost[i-2]) return b复杂度
- 时间:O(n),一遍线性递推,每格 O(1)
- 空间:O(1),只需前两项滚动
易错点
面试追问把动画讲成自己的话
追问为什么能滚动数组优化?
追问和爬楼梯 LC70 的关系?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
打家劫舍
LeetCode 198 · 中等 · 沿着 一维动态规划 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题