斐波那契数

力扣题目链接(opens new window)

斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1 给你n ,请计算 F(n) 。

示例 1:

  • 输入:2
  • 输出:1
  • 解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

  • 输入:3
  • 输出:2
  • 解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

  • 输入:4
  • 输出:3
  • 解释:F(4) = F(3) + F(2) = 2 + 1 = 3

提示:

  • 0 <= n <= 30

思路

本来这题应该是用递归写的,但作为DP的入门题也合适

先说明一下,DP五部曲:

  • 确定DP数组以及数组的下标定义
  • 确定递推公式
  • 确定DP数组如何初始化
  • 确定遍历顺序
  • 打印DP数组(主要用于debug)
五部曲分析

那下面就用这题来做一个示范

1、确定DP数组以及数组的下标定义

做动规题都需要先明确dp数组dp[i]的定义

在本题中,dp[i]应该指的是:第i个斐波那契数的数值是dp[i]

2、确定递推公式

因为刚刚入门,就目前我的理解,所谓的递推公式就是题目中的某种解决问题的思路的抽象形式

像这里,题目直接就给了F(n) = F(n - 1) + F(n - 2)

这个公式就是用来求斐波那契数的,那么这个公式也就是要找的递推公式(直接给出了所以本题简单)

结合本题dp[i]的定义可以得到最终需要的递推公式:dp[i] = dp[i - 1] + dp[i - 2]

3、确定DP数组初始化方式

题目也给了:F(0) = 0,F(1) = 1,所以初始化方式如下:

dp[0] = 0;
dp[1] = 1;

4、确定遍历顺序

因为dp[i] = dp[i - 1] + dp[i - 2],先有的dp[i - 1]和dp[i - 2]才能求dp[i]

故遍历顺序应该是从前向后

5、打印DP数组

这一步的意思就是,根据我们推断的递推公式,将dp[i]的值自行计算出来看看对不对

代码

class Solution {
public:
int fib(int n) {
if(n <= 1) return n;//如果n小于等于1,即直接得到第1个斐波那契数(0 + 1),因此直接返回n即可。
vector<int> dp(n + 1);//创建dp数组
dp[0] = 0;//初始化dp数组
dp[1] = 1; for(int i = 2; i <= n; ++i){//遍历
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];//返回dp数组中n处的值,即第n个斐波那契数
}
};

爬梯子

力扣题目链接(opens new window)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

  • 输入: 2
  • 输出: 2
  • 解释: 有两种方法可以爬到楼顶。
    • 1 阶 + 1 阶
    • 2 阶

示例 2:

  • 输入: 3
  • 输出: 3
  • 解释: 有三种方法可以爬到楼顶。
    • 1 阶 + 1 阶 + 1 阶
    • 1 阶 + 2 阶
    • 2 阶 + 1 阶

思路

先看看题目描述,要求的是爬到楼顶有多少种方法

再看一下示例2,

爬到第一层楼梯有1种方法;

爬到第二层楼梯有2种方法;(一阶一阶爬、一次爬两阶)

那么爬到第三层楼梯就有3种方法;(一阶一阶爬、一阶+二阶、二阶+一阶)

实际上到第三层楼梯的方法数可以通过到第一层和到第二层的方法数推出

那么就可以用dp

五部曲

还是五步走

1、确定dp数组的含义

dp[i]:爬到第i层楼梯有dp[i]种方法

2、确定递推公式

如何推出递推公式?要结合对于dp[i]的定义

1阶   1种  dp[i-2],上i-2层楼梯,有dp[i - 2]种方法(现在是i-2阶,再往上走1阶到i-1阶)
2阶 2种 dp[i-1],上i-1层楼梯,有dp[i - 1]种方法(现在是i-1阶,再往上走1阶到i阶)
3阶 3种 dp[i],上i层楼梯,有dp[i]种方法

好,到第i层楼梯(也就是第3层),按dp数组定义来是有dp[i]种方法

现在往回看,在i-2层时,我们可以选择一次爬两阶,然后上到第i层;同理在i-1层时,我们也可以选择一次爬一阶,然后上到第i层;

这说明,到达第i层楼梯的dp[i]种方法中包含着爬上i-2层和i-1层时的方法

由此可以总结出到达第i层楼梯的递推公式:dp[i] = dp[i - 2] + dp[i - 1]

3、确定dp数组的初始化方式

之前在 斐波那契数 中,我们讨论初始化时,是有对dp[0]进行初始化的

但是这里可以不用讨论dp[0],下面来说具体原因

在确定dp数组的初始化方式时,我们仍然要遵循第一步中给dp数组下的定义,即dp[i]:爬到第i层楼梯有dp[i]种方法

根据此定义来解释dp[0]就有点问题,爬到第0层楼有dp[0]种方法?

都0层楼了,相当于不用走直接在终点了,这样就可能会有多种解释

最好的办法就是不讨论dp[0]的情况,而且题目中说了n是一个正整数,根本就没说n有为0的情况。

那么根据dp数组的定义,我们可以得到以下初始化:

dp[1] = 1;
dp[2] = 2;

显然这是无争议,也是符合dp数组定义的

4、确定遍历顺序

分析到这里了,你肯定发现这题和斐波那契数其实是一样的

第i个数(阶梯)需要依靠第i-1和i-2个数(阶梯)确定

自然的,本题的遍历顺序也需要是从前向后

代码

因为我们不讨论dp[0],所以应该从dp[3]开始累加

步骤:

1、处理n小于等于1的情况

2、创建dp数组,并初始化

3、按照设定的顺序开始遍历累加dp数组,最后返回dp[n]

class Solution {
public:
int climbStairs(int n) {
if(n <= 1) return n;//如果n小于等于1,那么只有一种爬楼梯的方法,即直接到达目标楼层,因此直接返回n即可。
vector<int> dp(n + 1);//创建dp数组 dp[1] = 1;//初始化dp数组
dp[2] = 2;
for(int i = 3; i <= n; ++i){// 注意i是从3开始的
dp[i] = dp[i - 2] + dp[i - 1];
}
return dp[n];//返回dp数组中n处的值,即爬到第i层楼梯有dp[i]种方法
}
};

在这段代码中,如果n小于等于1,那么只有一种爬楼梯的方法,即直接到达目标楼层,因此直接返回n即可。

如果n大于1,那么创建一个大小为n+1的dp数组来记录每个楼梯台阶的爬楼梯方法数。然后初始化dp数组的前两个元素为1和2,因为到达第一层楼梯只有一种方法,到达第二层楼梯有两种方法。接着通过循环,从第三个元素开始依次计算每个楼梯的爬楼梯方法数,即dp[i] = dp[i - 2] + dp[i - 1],最后返回dp数组中n处的值,即到达第n层楼梯的爬楼梯方法数。

使用最小花费爬楼梯

力扣题目链接(opens new window)

旧题目描述

数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。

每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。

请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。

示例 1:

  • 输入:cost = [10, 15, 20]
  • 输出:15
  • 解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。

示例 2:

  • 输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
  • 输出:6
  • 解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。

提示:

  • cost 的长度范围是 [2, 1000]。
  • cost[i] 将会是一个整型数据,范围为 [0, 999] 。

思路

由题意,我们每向上爬一格阶梯(可以选择从第0阶或者第1阶开始,并且每次可以跳一格或两格),需要花费当前阶梯(也就是起跳台阶)规定的体力

例如示例1中,我们第一次爬一格到第二级阶梯,此处阶梯价格为15,那么目前花费就是15,然后再爬一格到达顶层,结束

总共花费就是15

从这个过程中可以发现:

​ 1、当前"站"在某个阶梯上是不扣体力的(例如最开始站在第1阶(15)上,不会扣15)

​ 2、只有当爬到某一台阶后才会扣取出发(起跳)台阶的标价(选择从第一阶出发,从第1阶跳两阶到第3阶,扣掉第一阶上的标价15)

​ 3、到达顶层之后,顶层不扣体力(还是拿示例1来说,这里的第2阶(20)不是顶层,顶层应该是第3阶)

题意差不多清楚之后,可以来看五部曲了

五步走

1、确定dp数组的定义

其实题目中就给了

dp[i]:到达第i个阶梯的最小体力花费dp[i]

2、确定递推公式

dp[i]  ↑ ↑
dp[i-1]↑ ↑//dp[i]可以由dp[i-1]跳了一步得到
dp[i-2]↑ //dp[i]也可以由dp[i-2]跳了两步得到

而跳跃之后需要花费起跳台阶上的对应体力消耗,所以可以得到以下式子

dp[i]
dp[i-1]+cost[i-1]
dp[i-2]+cost[i-2]

通过上面的式子可以得知,从i-1跳可以得到dp[i],从i-2跳也可以得到dp[i],取哪个呢?

因为题意让我们找的是最小体力花费,并且对于dp数组的定义也是如此

因此我们要取这两者中最小的一个,即:

dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);

这便是递推公式

3、确定dp数组的初始值

如何初始化dp数组呢?这里还需要分析

根据递推公式可知

dp[2]可以由dp[1]和dp[0]求得

dp[3]可以由dp[2]和dp[1]求得

...以此类推

可见,dp[1]和dp[0]是求数组中后续数值的基础,因此需要对dp[1]和dp[0]进行初始化

怎么初始化呢?

根据题意,我们可以从0阶或者1阶开始

又由前面的分析可知,我们"站"在某个位置是不需要花费体力的

那么不论从0阶还是1阶出发,只要还没开始跳,花费都是0(不管0、1阶处台阶标价为多少)

因此可以将dp[1]和dp[0]都初始化为0

4、确定遍历顺序

从递推公式看,后面的元素时依靠前面的元素来推导出来的,因此遍历顺序还是从前往后

代码

class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
//创建dp数组
vector<int> dp(cost.size() + 1);
//初始化dp数组
dp[0] = 0;
dp[1] = 0;
//遍历
for(int i = 2; i <= cost.size(); ++i){//从2开始,因为0、1都是可选的出发位置
dp[i] = min(dp[i - 2] + cost[i - 2], dp[i - 1] + cost[i - 1]);
}
return dp[cost.size()];//返回爬到第cost.size()个台阶时的体力消耗
}
};

【LeetCode动态规划#01】动规入门:求斐波那契数 + 爬楼梯 + 最小代价爬楼梯(熟悉解题方法论)的更多相关文章

  1. hdu1568&&hdu3117 求斐波那契数前四位和后四位

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1568 题意:如标题所示,求斐波那契数前四位,不足四位直接输出答案 斐波那契数列通式: 当n<=2 ...

  2. C++求斐波那契数

    题目内容:斐波那契数定义为:f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2)(n>1且n为整数) 如果写出菲氏数列,则应该是: 0 1 1 2 3 5 8 13 21 34 …… ...

  3. POJ 3070(求斐波那契数 矩阵快速幂)

    题意就是求第 n 个斐波那契数. 由于时间和内存限制,显然不能直接暴力解或者打表,想到用矩阵快速幂的做法. 代码如下: #include <cstdio> using namespace ...

  4. HDU 1568 Fibonacci【求斐波那契数的前4位/递推式】

    Fibonacci Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Proble ...

  5. 求斐波那契数的python语言实现---递归和迭代

    迭代实现如下: def fab(n): n1 = 1 n2 = 1 if n<1: print("输入有误!") return -1 while (n-2)>0: n3 ...

  6. 数学算法(一):快速求斐波那契数第n项通过黄金分割率公式

    有一个固定的数学公式= =,不知道的话显然没法应用 首先黄金分割率接近于这个公式, (以下为黄金分割率与斐波那契的关系,可跳过) 通过斐波那契数列公式 两边同时除以 得: (1) 注意后一项比前一项接 ...

  7. 算法笔记_001:斐波那契数的多种解法(Java)

    本篇文章解决的问题来源于算法设计与分析课程的课堂作业,主要是运用多种方法来计算斐波那契数.具体问题及解法如下: 一.问题1: 问题描述:利用迭代算法寻找不超过编程环境能够支持的最大整数的斐波那契数是第 ...

  8. LeetCode.509——斐波那契数

    问题描述: 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0, F(1) = 1 F(N) ...

  9. 用x种方式求第n项斐波那契数,99%的人只会第一种

    大家好啊,我们又见面了.听说有人想学数据结构与算法却不知道从何下手?那你就认真看完本篇文章,或许能从中找到方法与技巧.     本期我们就从斐波那契数列的几种解法入手,感受算法的强大与奥妙吧. 原文链 ...

  10. C# 求斐波那契数列的前10个数字 :1 1 2 3 5 8 13 21 34 55

    //C# 求斐波那契数列的前10个数字 :1 1 2 3 5 8 13 21 34 55 using System; using System.Collections.Generic; using S ...

随机推荐

  1. [转帖]Linux磁盘I/O(二):使用vm.dirty_ratio和vm.dirty_background_ratio优化磁盘性能

    文件缓存是一项重要的性能改进,在大多数情况下,读缓存在绝大多数情况下是有益无害的(程序可以直接从RAM中读取数据).写缓存比较复杂,Linux内核将磁盘写入缓存,过段时间再异步将它们刷新到磁盘.这对加 ...

  2. [转帖] 这grep咋还不支持\d呢(BRE,ERE,PCRE)

    https://www.cnblogs.com/codelogs/p/16060372.html 简介# 对于刚使用Linux不久的同学,肯定会遇到这个问题,就是用grep匹配数字时,发现\d匹配不了 ...

  3. IIS 实现autoindex的简单方法 能够下载文件等.

    之前使用nginx 的autoindex on 的参数 能够实现了 nginx的 目录浏览查看文件 但是那是linux上面的 windows 上面很多 使用的 其实是 iis的居多 然后看了下 其实也 ...

  4. CentOS7上面一键部署rabbitmq的简单方法

    1. rabbitmq的安装部署比redis之类的要麻烦一些. 主要是他是基于erlang写的 而不是基于c或者是c++写的 2. 很多时候编译需要添加很多组件, 但是一些机器可能不会让添加这么多的组 ...

  5. js中toFixed 并不是你想的那样进行四舍五入

    toFixed 的简单介绍 toFixed() 方法可把 Number 类型的数字通过四舍五入为指定小数位的字符串.(将数字类型转化为字符串类型) 也就是说toFixed只能够处理数字类型的. 字符串 ...

  6. 初试高云FPGA

    前言 之前一直眼馋Sipeed的Tang系列,正好遇到有工程需要高速控制并行总线,就买了NANO 9K和Primer 20K试试水 买回来先拆的贵的20k,结果发现Sipeed设计师有奇怪的脑回路: ...

  7. 微信小程序网页嵌入开发

    无脑开发 下载微信开发者工具 新建一个项目找到index开头的进去全选删除粘贴下面代码 <!-- html --> <!-- 指向嵌入外部链接的地址 --> <web-v ...

  8. windows幻灯片壁纸

    设置为10秒 win+r输入regedit 查找路径 HKEY_CURRENT_USER\Control Panel\Personalization\Desktop Slideshow 修改inter ...

  9. Leetcode 面试题22. 链表中倒数第k个节点 Java语言求解

    题目链接 https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/ 题目内容 输入一个链表,输出该链 ...

  10. 《Spring 手撸专栏》| 开篇介绍,我要带新人撸 Spring 啦!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 不正经!写写面经,去撸Spring源码啦? 是的,在写了4篇关于Spring核心源码 ...