746. 使用最小花费爬楼梯(Min Cost Climbing Stairs)¶
频次 ★★★ · 难度 🟢 · 高频:字节
题目¶
数组 cost[i] 表示从第 i 阶向上爬的花费,可从第 0 或第 1 阶开始,每次爬 1 或 2 阶,求到达楼顶的最小总花费。
示例:
思路¶
DP:dp[i] 表示到达第 i 阶的最小花费。
dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])
楼顶在数组末尾的下一个位置,即 dp[n] 是答案。可优化到 O(1) 空间(两个变量滚动)。
代码¶
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;
int a = 0, b = 0; // dp[0], dp[1](从 0 或 1 开始,花费为 0)
for (int i = 2; i <= n; i++) {
int c = Math.min(a + cost[i - 2], b + cost[i - 1]);
a = b;
b = c;
}
return b;
}
到达付 / 离开付:两种流派与虚拟楼顶返回值¶
| 流派甲:到达付 | 流派乙:离开付(wiki 参考解) | |
|---|---|---|
| dp[i] 语义 | 踩上 i 且付过 cost[i] 的最小总费 | 已越过 i(在 i 处付完、准备上爬)的最小总费 |
| 初始化 | dp[0]=cost[0], dp[1]=cost[1] | dp[0]=0, dp[1]=0(站 0/1 免费) |
| 转移 | dp[i] = cost[i] + min(dp[i-1], dp[i-2]) | dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]) |
| 答案 | min(dp[n-1], dp[n-2]) | dp[n](数组开 n+1,楼顶是虚层、cost 不付) |
两流派输出恒等、代码全队形不同(错位一档)——选定后初始化/循环/返回值全套一致,别中途换。
常见 WA(实测踩坑):返回 dp[n-1]¶
到达付流派下返回 dp[n-1] 只算了"最后 Konto at last"的路径,漏掉"从 last-1 直跳上楼"——楼顶是虚拟层,最后一跳免费。修正后 [10,15,20]: 30✗ → 15✓、[1,100]: 100✗ → 1✓。且对有些用例碰巧相等(dp[last] 恰是 min),随机挂正因此。
复杂度¶
- 时间:O(n)
- 空间:O(1)
边界条件¶
- cost 长度 2:直接取 min(cost[0], cost[1])
- 从 0 或 1 开始不花钱——dp[0]=dp[1]=0
变式¶
- 70. 爬楼梯:不带 cost 的版本,dp[i]=dp[i-1]+dp[i-2]
- 三步问题:每次可爬 1~3 阶,dp[i]=dp[i-1]+dp[i-2]+dp[i-3]
易错点¶
- 楼顶在
cost.length位置(不是cost.length - 1),dp 数组长度为 n+1 - dp[0] 和 dp[1] 初始化为 0(站在起点不花钱,花费用在"离开"时)
面试追问¶
- 如果花费在"到达"而不是"离开"? 转移方程会变,讨论清楚题意再写
关联题¶
- 同套路:70. 爬楼梯 —— 无 cost 基础版
- 进阶:509. 斐波那契数 —— DP 入门
- 知识点:带权 DP 的滚动优化见动态规划