Day38【动态规划】509.斐波那契数、70.爬楼梯、746.使用最小花费爬楼梯

理论基础

文章讲解 

视频讲解 

动态规划五部曲: 

  1. 确定dp数组下标及值的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序(遍历循环外层是啥内层是啥,遍历从左向右还是从右向左)
  5. 打印dp数组,用于 debug

509.斐波那契数 

力扣题目链接/文章讲解 

视频讲解 

严格按照动态规划五部曲进行思考分析! 

1、确定dp数组下标及值的含义

dp[i]:下标 i 表示第 i 个 Fibonacci 数,dp[i] 的值表示第 i 个 Fibonacci 数的数值为 dp[i](i 从 0 开始)

2、确定递推公式

题目描述其实已经告诉我们递推公式了:dp[i] = dp[i - 1] + dp[i - 2]

3、dp 数组初始化

根据递推公式,至少需要初始化两个数值。题目也告诉我们了:dp[0] = 1, dp[1] = 1

4、确定遍历顺序

从递归公式 dp[i] = dp[i - 1] + dp[i - 2] 中可以看出,dp[i] 是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的,这样 dp[i] 所依赖的 dp[i - 1] 和 dp[i - 2] 才是已经被更新过的值

5、打印 dp 数组验证

注意,上面是我们的思考过程,如何初始化是依赖于我们的递归公式的。但是在代码实现中,需要先初始化,再利用递推公式更新 dp 数组 

代码如下 

class Solution {
public:
    int fib(int n) {
        if (n <= 1) return n;
        vector<int> dp(n + 1);  //  i表示第i个Fibonacci数,dp[i]表示第i个Fibonacci数的值
        // 递推公式从题目中获得:dp[i] = dp[i - 1] + dp[i - 2]
        // 初始化从题目中获得:dp[0] = 0, dp[1] = 1;
        dp[0] = 0;
        dp[1] = 1;
        for (int i = 2; i <= n; ++i) {  // 遍历顺序从前向后
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        // 打印dp数组验证,略
        return dp[n];   // 根据题意与dp数组的含义,最后的结果就是dp[n]的值
    }
};

70.爬楼梯 

力扣题目链接/文章讲解

视频讲解 

1、确定 dp 数组下标及值的含义 

dp[i]:下标 i 表示位于第 i 阶楼梯,dp[i] 的值表示爬到第 i 阶楼梯的方法数为 dp[i]

2、确定递推公式

想求dp[i],这个 i 表示位于第 i 阶楼梯,根据题目描述,可以从第 i - 1 阶爬一步到第 i 阶,也可以从第 i - 2 阶爬两步到第 i 阶。因此,爬到第 i 阶楼梯的方法数 dp[i] 等于爬到第 i - 1 阶楼梯的方法数 dp[i - 1] 加上爬到第 i - 2 阶楼梯的方法数 dp[i - 2],即 dp[i] = dp[i - 1] + dp[i - 2] 

在推导 dp[i] 的时候,一定要时刻想着 dp 数组下标及值的含义,否则容易跑偏 

3、dp 数组初始化

不考虑 dp[0] 如何初始化,只初始化 dp[1] = 1,dp[2] = 2,然后从 i = 3 开始递推,这样才符合 dp[i] 的定义  

dp[1],即爬到第 1 阶的方法数应该为 1,dp[1] = 1

dp[2],即爬到第 2 阶的方法数应该为 2(一次爬 1 阶或者直接爬 2 阶),dp[2] = 2

4、确定遍历顺序

从递归公式 dp[i] = dp[i - 1] + dp[i - 2] 中可以看出,dp[i] 是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的,这样 dp[i] 所依赖的 dp[i - 1] 和 dp[i - 2] 才是已经被更新过的值

5、打印 dp 数组验证 

代码如下 

class Solution {
public:
    int climbStairs(int n) {
        if (n == 1) return 1;    
        // 1. 确定dp数组下标及值的含义:下标i表示位于第i个台阶,dp[i]表示爬到第i个台阶的方法数
        vector<int> dp(n + 1);
        // 2. 确定递推公式:dp[i] = dp[i-1] + dp[i-2]
        // 3. 初始化dp数组:我们不考虑dp[0]如何初始化,只考虑初始化dp[1]和dp[2],后续利用递归公式更新dp数组时从下标3开始更新即可
        dp[1] = 1;
        dp[2] = 2;
        // 4. 确定遍历顺序:从前向后遍历,这样才能够利用前两个已被更新的值
        for (int i = 3; i <= n; ++i)
            dp[i] = dp[i - 1] + dp[i - 2];
        // 5. 打印dp数组验证,略
        return dp[n];   // 根据题意与dp数组的含义,最后的结果就是dp[n]的值
    }
};

746.使用最小花费爬楼梯

力扣题目链接/文章讲解 

视频讲解 

注意本题跳的时候才需要花费 

1、确定 dp 数组下标及值的含义 

dp[i]:下标 i 表示位于第 i 个台阶,dp[i] 的值表示爬到第 i 个台阶的最小花费为 dp[i]

2、确定递推公式

想求dp[i],这个 i 表示位于第 i 个台阶,根据题目描述,可以从第 i - 1 阶爬一步到第 i 阶,也可以从第 i - 2 阶爬两步到第 i 阶。因此,爬到第 i 阶的最小花费 dp[i] 等于爬到第 i - 1 阶的最小花费 dp[i - 1] 加上 cost[i - 1] 与爬到第 i - 2 阶的最小花费 dp[i - 2] 加上 cost[i - 2] 中的最小值,即 dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])

在推导 dp[i] 的时候,一定要时刻想着 dp 数组下标及值的含义,否则容易跑偏 

3、dp 数组初始化

根据递推公式,至少需要初始化两个数值

可以选择从下标 0 的台阶开始跳,故爬到台阶 0 所需要的最小花费为 0,即 dp[0] = 0

也可以选择从下标 1 的台阶开始跳,故爬到台阶 1 所需要的最小花费为 0,即 dp[1] = 0

4、确定遍历顺序

从递归公式可以看出,dp[i] 依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的,这样 dp[i] 所依赖的 dp[i - 1] 和 dp[i - 2] 才是已经被更新过的值

5、打印 dp 数组验证 

代码如下

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        // 1、确定dp[i]含义:i表示位于台阶i,dp[i]表示到达台阶i的最小花费
        vector<int> dp(cost.size() + 1);
        // 2、确定递推公式:从台阶i-1能够爬到台阶i,从台阶i-2也能够爬到台阶i,到达台阶i的最小花费为这两种方案花费的最小值。dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
        // 3、初始化,根据题意,dp[0] = dp[1] = 0;
        dp[0] = dp[1] = 0;
        // 4、确定遍历顺序,从前向后遍历
        for (int i = 2; i <= cost.size(); ++i)
            dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        // 5、打印dp数组略
        return dp[cost.size()]; // 根据题意,结果就是dp[cost.size()]
    }
};

回顾总结 

动态规划五部曲的思维模式 

注意我们确定dp数组的下标及值的含义时,下标含义值的含义分开定义的,想想为什么要这样(提示两个字:状态)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,针对您的问题,我来讲一下动态规划楼梯问题5个台阶的分析方法。 在动态规划中,我们通常会使用一个数组来存储子问题的解,以便后续的子问题可以直接使用已解决的子问题的解,从而避免重复计算。对于楼梯问题,我们可以使用一个一维数组来存储到达每个台阶的不同路径数量。 以下是对于5个台阶的分析方法: 1. 递推法:根据题目可知,到达第n个台阶的方法数为到达第n-1和第n-2个台阶的方法数之和,因此可以使用递推的方式来求解。具体实现可以使用一个一维数组dp来存储到达每个台阶的方法数,从dp[0]开始逐个计算,最终返回dp[n]即可。 2. 递归法:类似于递推法,也是通过递归的方式来求解。递归终止条件为到达第0个台阶和第1个台阶的方法数分别为1和1,递归求解到达n-1和n-2个台阶的方法数,最终返回它们之和即可。 3. 记忆化搜索法:在递归法的基础上,加入了记忆化的思想,即在求解每个子问题时,先查看该子问题是否已经计算过,如果已经计算过,则直接返回已有的解。如果没有计算过,则递归求解,并将结果存储到一个数组中,以便后续的子问题可以直接使用已解决的子问题的解。 4. 斐波数列法:利用斐波数列的递推公式,即f(n)=f(n-1)+f(n-2),来求解楼梯问题。具体实现可以使用两个变量f1和f2来存储f(n-1)和f(n-2)的值,然后依次更新它们的值,最终返回f(n)即可。 5. 矩阵快速幂法:在斐波数列法的基础上,利用矩阵快速幂的思想,可以将时间复杂度从O(n)降低到O(logn)。具体实现可以将斐波数列的递推公式转化为矩阵的形式,然后使用矩阵快速幂的方式来求解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

林沐华

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

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

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

打赏作者

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

抵扣说明:

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

余额充值