【动态规划:斐波那契数列模型】使用最小花费爬楼梯(easy)

在这里插入图片描述

使用最小花费爬楼梯(easy)

746. 使用最小花费爬楼梯

​ 给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

​ 请你计算并返回达到楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。

示例 2:

输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。

提示:

  • 2 <= cost.length <= 1000
  • 0 <= cost[i] <= 999

解题思路一

​ 这道题比较容易踩坑的就是这个楼顶到底是在哪里❓❓❓

​ 其实数组内的每⼀个下标 [0, n - 1] 标识的都是楼层,而 顶楼的位置其实是在 n 的位置,这点非常重要!!!

​ 所以我们就必须 要把数组开辟到 dp[n] 的位置,这和后面的解题思路二是不太一样的!

在这里插入图片描述

​ 接下来就是按我们的动态规划套路走了,解题思路一主要考虑的是以 i 为终点的状态表示

​ 首先就是确定 dp[i] 的状态表示什么,因为这道题说要求到达顶层花费的最小费用,那么很明显,dp[i] 就可以表示到达 i 层的时候花费的最小费用,注意是最小费用哦!

接着就是状态转移方程,思想如下:

  • 用之前或者之后的状态,推导出 dp[i] 的值。
  • 根据最近的一步来划分问题。

​ 题目说每次可以跨越两步,也就是说到达第 i 层的只能是通过 i-1 或者 i-2 层来到达,所以我们求解 dp[i] 肯定是和 dp[i-1]dp[i-2] 有关系!

​ 对的,先说一下从 i-1 层到 i 层的关系,很明显,dp[i-1] 表示到达 i-1 层花费的最小费用,那么现在到达 i 层,只需要加上 i-1 花费的费用,也就是 cost[i-1] 即可!对于 i-2 层也是如此,接着既然要求最小花费,那么我们只需要在这两个到达 i 层费用中取那个最小费用的那个即可!

​ 所以状态转移方程:dp[i] = min( dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])

​ 然后就是初始化问题,因为填 dp 表过程中,我们是不需要知道第 0 层和第 1 层的,它们其实默认为 0,表示为起跳层,花费 0 元,所以我们将 dp[0]dp[1] 赋值为 0 即可!

​ 接着就是填表的顺序,因为我们是通过 i-1 层和 i-2 层来推出 i 层的,所以 填表顺序是从左往右的

​ 最后就是返回值,很明显就是最后推出来的 dp[n],这和等会要讲的解题思路二不太一样!

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        // 创建dp表,dp[i]表示到达该层花费最小的费用
        int n = cost.size();
        vector<int> dp(n + 1);

        // 初始化,由题意可知0和1层开始爬楼梯,所以不需要支付费用
        // 但其实vector已经帮我们默认初始化为0了,所以这里也可以不去初始化
        dp[0] = dp[1] = 0;

        // 填表
        for(int i = 2; i <= n; ++i)
        {
            // 因为每次只能胯两步,所以要求前两步中花费最小的那步
            dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);
        }
        return dp[n]; 
    }
};

解题思路二

​ 题解二相当于是题解一的逆版本,也就是说我们要从后往前去推导出结果!

​ 其实最关键的就是这个状态表示的改变,这次 dp[i] 表示的是从第 i 层到达楼顶所需要花的最小费用,这和题解一表示的达到第 i 层花费的最小费用是有区别的!因为状态这样子表示了,那么我们要求到达楼顶花费的最小费用,并且我们是从第 0 层和第 1 层出发的,那么意思就是我们 要求 dp[0] 和 dp[1] 中小的那个,对不对❗ 这其实也顺带的看出来了我们最后的返回值,就是 min(dp[0], dp[1])

​ 因为最后是通过 dp[0]dp[1] 来获得最小费用,所以 没必要创建 n+1 个空间

​ 然后就是状态转移方程,既然我们最后要求的是 dp[0]dp[1],那么势必要从 后往前推导,这里以 dp[i] 为例,它表示从第 i 层出发到楼顶最小费用,那么其实我们就能想到,第 i 层是可以向后跨越一步或者两步的,那么也就是说第 i+1 层和 i+2 层的费用是会影响到第 i 层的,因为我们要取的是最小费用,所以我们只需要取到 i+1i+2 层中到达楼顶花的费用最小的那个,再加上当前第 i 层跳跃到 i+1 层或者 i+2 层需要花费的费用,就等于从第 i 层到达楼顶需要花的最小费用了!

​ 也就是说状态转移方程是这样子的:dp[i] = min(dp[i+1], dp[i+2]) + cost[i]

​ 其次填表的顺序是从后往前推导,这个我们是知道的,但是这个时候我们就要注意一点细节了,就是初始化的问题,因为要从后往前推,那么我们就得先将最后面的值进行初始化!

​ 因为每次最多跨越两步,所以我们其实是可以先确定后两层到楼顶的最小费用的!为什么呢,因为 n-1 层到楼顶就是一步的距离,直接跳过去是最小费用的,而对于 n-2 层来说,没必要先跳到 n-1 层,因为可以一次跳两步,直接跳到楼顶即是最小费用!

​ 所以这两层花费的最小费用其实就是它们这一跳的费用!即 dp[n-1] = cost[n-1],dp[n-2] = cost[n-2]在这里插入图片描述

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        // 创建dp表,dp[i]表示从该层出发,到达楼顶需要花费的最小费用,注意和题解一区分开!!!
        // 并且我们是从i-1层出发从右往左推导,所以不需要创建n+1个空间
        int n = cost.size();
        vector<int> dp(n);

        // 初始化,因为我们是从右往左推导,所以要先初始化的是最后面两层
        // 并且最后两层都有一个特点,它们到达楼顶的最小花费就是直接跳到楼顶,不需要计算其它的
        // 尤其是要理解dp[n-2],它没必要说跳到n-1再去跳到楼顶,直接跳到楼顶即可!
        dp[n-1] = cost[n-1];
        dp[n-2] = cost[n-2];

        // 填表,顺序是从右往左
        for(int i = n - 3; i >= 0; --i)
        {
            // 每次取下一点中花费最小的一点
            dp[i] = min(dp[i+1], dp[i+2]) + cost[i];
        }

        // 返回值要求的不是楼顶,而是前两层dp表中的最小值
        // 因为dp[i]表示从该层出发到楼顶花费的最小费用,和题解一不一样!
        return min(dp[0], dp[1]); 
    }
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

利刃大大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值