【LeetCode动态规划#01】动规入门:求斐波那契数 + 爬楼梯 + 最小代价爬楼梯(熟悉解题方法论)
斐波那契数
斐波那契数,通常用 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个斐波那契数
}
};
爬梯子
假设你正在爬楼梯。需要 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层楼梯的爬楼梯方法数。
使用最小花费爬楼梯
旧题目描述:
数组的每个下标作为一个阶梯,第 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】动规入门:求斐波那契数 + 爬楼梯 + 最小代价爬楼梯(熟悉解题方法论)的更多相关文章
- hdu1568&&hdu3117 求斐波那契数前四位和后四位
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1568 题意:如标题所示,求斐波那契数前四位,不足四位直接输出答案 斐波那契数列通式: 当n<=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 …… ...
- POJ 3070(求斐波那契数 矩阵快速幂)
题意就是求第 n 个斐波那契数. 由于时间和内存限制,显然不能直接暴力解或者打表,想到用矩阵快速幂的做法. 代码如下: #include <cstdio> using namespace ...
- HDU 1568 Fibonacci【求斐波那契数的前4位/递推式】
Fibonacci Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Proble ...
- 求斐波那契数的python语言实现---递归和迭代
迭代实现如下: def fab(n): n1 = 1 n2 = 1 if n<1: print("输入有误!") return -1 while (n-2)>0: n3 ...
- 数学算法(一):快速求斐波那契数第n项通过黄金分割率公式
有一个固定的数学公式= =,不知道的话显然没法应用 首先黄金分割率接近于这个公式, (以下为黄金分割率与斐波那契的关系,可跳过) 通过斐波那契数列公式 两边同时除以 得: (1) 注意后一项比前一项接 ...
- 算法笔记_001:斐波那契数的多种解法(Java)
本篇文章解决的问题来源于算法设计与分析课程的课堂作业,主要是运用多种方法来计算斐波那契数.具体问题及解法如下: 一.问题1: 问题描述:利用迭代算法寻找不超过编程环境能够支持的最大整数的斐波那契数是第 ...
- LeetCode.509——斐波那契数
问题描述: 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0, F(1) = 1 F(N) ...
- 用x种方式求第n项斐波那契数,99%的人只会第一种
大家好啊,我们又见面了.听说有人想学数据结构与算法却不知道从何下手?那你就认真看完本篇文章,或许能从中找到方法与技巧. 本期我们就从斐波那契数列的几种解法入手,感受算法的强大与奥妙吧. 原文链 ...
- 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 ...
随机推荐
- 准备学习 make
make -h用法:make [选项] [目标] ...选项: -b, -m 为兼容性而忽略. -B, --always-make 无条件制作 (make) 所有目标. -C 目录, --direct ...
- 学习: Linux的 date 命令
date 命令非常好用 多用 date --h 还是非常好的 获取 今天是今年的第多少天 最简单的办法 就是 date +%j 以后需要多学习 多利用 linux的帮助才可以呢. Usage: dat ...
- 使用systemd管理多nginx服务以及单nginx服务实现多vhost访问的操作步骤
背景 nginx是开源的web服务器, 性能与可配置性和插件做的非常完善. 可以使用简单的命令拉起来nginx进行服务提供,但是有时候需要使用keepalive等软件实现保活,以及实现开启启动等,比较 ...
- 一种轻量分表方案-MyBatis拦截器分表实践
背景 部门内有一些亿级别核心业务表增速非常快,增量日均100W,但线上业务只依赖近一周的数据.随着数据量的迅速增长,慢SQL频发,数据库性能下降,系统稳定性受到严重影响.本篇文章,将分享如何使用MyB ...
- BigDecimal详解和精度问题
JavaGuide :「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识. BigDecimal 是大厂 Java 面试常问的一个知识点. <阿里巴巴 Java 开发 ...
- 【一】分布式训练---单机多卡多机多卡(飞桨paddle1.8)
1.分布式训练简介 分布式训练的核心目的: 加快模型的训练速度.通过对训练任务按照一定方法拆分分配到多个计算节点进行计算,再按照一定的方法对需要汇总的信息进行聚合,从而实现加快训练速度的目的. 1.1 ...
- 热门数据集提供【MNIST、鸢尾花、猫狗、CIFAR10、vegetables、Ox-Flowers17、pascalvoc】
热门数据集提供[MNIST.鸢尾花.猫狗.CIFAR10.vegetables.Ox-Flowers17.pascalvoc] 简介: 鸢尾花数据集: 约150条数据,每条样本4个属性,共3个类别 M ...
- 【深度学习项目二】卷积神经网络LeNet实现minst数字识别
相关文章: [深度学习项目一]全连接神经网络实现mnist数字识别 [深度学习项目二]卷积神经网络LeNet实现minst数字识别 [深度学习项目三]ResNet50多分类任务[十二生肖分类] 『深度 ...
- C++ LibCurl实现Web隐藏目录扫描
LibCurl是一个开源的免费的多协议数据传输开源库,该框架具备跨平台性,开源免费,并提供了包括HTTP.FTP.SMTP.POP3等协议的功能,使用libcurl可以方便地进行网络数据传输操作,如发 ...
- C++11之函数对象
目录 1.使用场景 2.函数对象 3.std::bind 4.总结 1.使用场景 在没有C++11的时候,我们通常使用回调函数来完成某些特定的功能,使用回调函数就需要先声明函数指针 示例: typed ...