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
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

林沐华

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

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

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

打赏作者

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

抵扣说明:

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

余额充值