给你一个整数数组 cost
,其中 cost[i]
是从楼梯第 i
个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0
或下标为 1
的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
思路
和 爬楼梯 有些类似,但爬楼梯中是求爬到第 i 个台阶有多少种方法,而本题是每个台阶都有一个cost值,若从第 i 个台阶向上爬 1 或 2 步则需要花费 cost[i] ,要求爬到顶楼的最小花费。题目提示可以从下标为 0 或 1 处的楼梯开始爬,根据Carl哥的解法,可以看作爬上第 1 和 第 2 个台阶不需要花费,当从第 1 或第 2 个台阶往上爬时才花费,那么情况就如下图:
下标为 0 和 1 处 dp[0] 和 dp[1] 都可以作为起点,站在这两个台阶(爬到这两个台阶)上无需花费,而当往上爬时,例如要爬上 i = 2 的台阶处,可以从两个台阶爬上来:台阶 i = 1 和台阶 i = 0。那么爬到 i = 2 的台阶处需要的最小花费即取 从台阶 i = 1 处爬 1 步 和 从台阶 i = 0 处爬 2 步 两种来源的花费的最小值;那么到达顶楼则来源于到达顶楼的 前面两个台阶的最小花费的最小值(所以dp数组需要比cost数组多一个长度用来放到达顶楼的最小花费,可以视为 顶楼的 cost[i] 为 0)。
除了以上这种理解,也可以看作 第一步是需要花费的,而最后的楼顶处则不需要花费(cost = 0),即我们到达第 i 下标的台阶时需要的最小花费来源于 前面紧接着的两个台阶的最小花费的最小值 再加上第 i 下标的台阶需要的花费 cost[i],到达楼顶时本来应该是前面两个台阶的最小花费的最小值加上顶楼的花费cost,但因为其为 0 ,故最终结果取最后面两个台阶的 dp 的最小值即为答案。
下面按照动态规划五部曲解题:
确定 dp 数组的含义
对于第一步不花费的情况和第一步花费的情况:dp[i] 表示到达下标为 i 的台阶需要的最小花费;
确定递推公式
从上面的分析已经可以较清楚地得出两种情况下的递推公式:
- 第一步不花费:第一步不花费隐含着后面到达某一个台阶时的最小花费 = min( 前面两个台阶的最小花费 ) 而无需加上当前爬上的台阶的 cost,大白话讲就是我到达这个台阶我不用“支付”脚下这个台阶的钱,因为我还没继续往上爬,我只是到达了当前的台阶。故而递推公式为:dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i])
- 第一步要花费:第一步要花费隐含着后面到达某一个台阶时的最小花费 = min( 前面两个台阶的最小花费 ) 并加上当前爬上的台阶的 cost。可以想象成:到达某一个台阶是需要收费的(就像收门票一样,不给钱就不让你上本台阶),而顶楼则不用 “收门票”。故而递推公式为:dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]
初始化 dp 数组
从上面分析可以清楚得出两种情况的dp初始化:
//情况1:第一步不收费
dp[0] = 0;
dp[1] = 0;
//情况2:第一步要收费
dp[0] = cost[0];
dp[1] = cost[1];
确定遍历顺序
由两种情况的递推公式:
//情况1 第一步不收费
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
//情况2 第一步要收费
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
可以看到,dp[i] 来自 dp[i - 1] 和 dp[i - 2],因此遍历顺序为从左往右。
举例推导 dp 数组
对于情况1:
对于情况2:
代码实现
//情况1 第一步不收费
class Solution {
public int minCostClimbingStairs(int[] cost) {
int[] dp = new int[cost.length + 1];
dp[0] = 0;
dp[1] = 0;
for(int i = 2; i <= cost.length; i++) {
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
}
return dp[cost.length];
}
}
//情况2 第一步要收费
class Solution {
public int minCostClimbingStairs(int[] cost) {
int[] dp = new int[cost.length];
dp[0] = cost[0];
dp[1] = cost[1];
for(int i = 2; i < cost.length; i++) {
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
}
return Math.min(dp[cost.length - 1], dp[cost.length - 2]);
}
}
《代码随想录》刷题记