这个爬楼梯的问题和斐波那契数列问题很像。 读完题大家应该知道指定需要动态规划的,贪心是不可能了。 本题在动态规划里相对简单一些,以至于大家可能看个公式就会了,但为了讲清楚思考过程,我按照我总结的动规四步曲来详细讲解: 1. 确定dp数组以及下标的含义 2. 确定递推公式 3. dp数组如何初始化 4. 确定遍历顺序 * 确定dp数组以及下标的含义 使用动态规划,就要有一个数组来记录状态,本题只需要一个一维数组dp[i]就可以了。 dp[i]的定义:第i个台阶所花费的最少体力为dp[i]。 **对于dp数组的定义,大家一定要清晰!** * 确定递推公式 **可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[i-2]**。 那么究竟是选dp[i-1]还是dp[i-2]呢? 一定是选最小的,所以dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; **注意这里为什么是加cost[i],而不是cost[i-1],cost[i-2]之类的**,因为题目中说了:第i个阶梯对应着一个非负数的体力花费值 cost[i] * dp数组如何初始化 根据dp数组的定义,dp数组初始化其实是比较难的,因为不可能初始化为第i台阶所花费的最少体力。 那么看一下递归公式,dp[i]由dp[i-1],dp[i-2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就够了,其他的最终都是dp[0]dp[1]推出。 所以初始化代码为: ``` vector dp(cost.size()); dp[0] = cost[0]; dp[1] = cost[1]; ``` * 确定遍历顺序 最后一步,递归公式有了,初始化有了,如何遍历呢? 本题的遍历顺序其实比较简单,简单到很多同学都忽略了思考这一步直接就把代码写出来了。 因为是模拟台阶,而且dp[i]又dp[i-1]dp[i-2]推出,所以是从前到后遍历cost数组就可以了。 但是稍稍有点难度的动态规划,其遍历顺序并不容易确定下来。 例如01背包,都知道两个for循环,一个for遍历物品嵌套一个for遍历背包容量,那么为什么不是一个for遍历背包容量嵌套一个for遍历物品呢? 以及在使用一维dp数组的时候遍历背包容量为什么要倒叙呢? 这些都是遍历顺序息息相关。 公众号「代码随想录」后面在讲解动态规划的时候,还会详细说明这些细节,大家可以微信搜一波「代码随想录」,关注后就会发现相见恨晚! 分析完动规四步曲,整体C++代码如下: ```C++ // 版本一 class Solution { public: int minCostClimbingStairs(vector& cost) { vector dp(cost.size()); dp[0] = cost[0]; dp[1] = cost[1]; for (int i = 2; i < cost.size(); i++) { dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; } return min(dp[cost.size() - 1], dp[cost.size() - 2]); } }; ``` * 时间复杂度:O(n) * 空间复杂度:O(n) 当然还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下: ```C++ // 版本二 class Solution { public: int minCostClimbingStairs(vector& cost) { vector dp(cost.size()); int dp0 = cost[0]; int dp1 = cost[1]; for (int i = 2; i < cost.size(); i++) { dp[i] = min(dp0, dp1) + cost[i]; dp0 = dp1; // 记录一下前两位 dp1 = dp[i]; } return min(dp[cost.size() - 1], dp[cost.size() - 2]); } }; ``` * 时间复杂度:O(n) * 空间复杂度:O(n) 当然我不建议这么写,能写出版本一就可以了,直观简洁!