LeetCode刷题day52

简介: LeetCode刷题day52

509. 斐波那契数


题目描述


斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n)

示例 1:


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


示例 2:


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


示例 3:


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


思路分析


动规五部曲


这里我们要用一个一维dp数组来保存递归的结果


  • 确定dp数组以及下标的含义


dp[i]的定义为:第i个数的斐波那契数值是dp[i]


  • 确定递推公式


题目已经把递推公式直接给我们了:状态转移方程 dp[i] = dp[i - 1] + dp[i - 2];


  • dp数组如何初始化


题目中把如何初始化也直接给我们了,如下: dp[0] = 0; dp[1] = 1;


  • 确定遍历顺序


从递归公式dp[i] = dp[i - 1] + dp[i - 2]; 中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的


  • 举例推导dp数组


按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:


0 1 1 2 3 5 8 13 21 34 55


如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的。


参考代码


方法一:数组模拟动归

int fib(int n) {
  vector<int> dp(31);//初始化 
  dp[0] = 0;
  dp[1] = 1;
  for(int i = 2;i <= n;i++){
    dp[i] = dp[i-1]+dp[i-2];//该式也是状态转移方程式 
  }
  return dp[n];
}

方法二:变量模拟

int fib(int n){
  if(n<=1){
    return n;
  }
  vector<int> dp(2);//定义
  dp[0] = 0;
  dp[1] = 1; 
  for(int i = 2;i<=n;i++){
    int sum = dp[0]+dp[1];
    dp[0] = dp[1];
    dp[1] = sum;
  }
  return dp[1];
} 

方法三:递归

//递归写法 
int fib(int n){
  if(n==1||n==0){
    return n;
  } 
  return fib(n-1)+fib(n-2);
}


70. 爬楼梯


题目描述


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

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


示例 1:


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


示例 2:


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


思路分析


看到这个题我们可以举几个例子


  • 楼梯1: 1 ==> 1种情况
  • 楼梯2 1+1 2 ==> 2种情况
  • 楼梯3 1+2 2+1 1+1+1 ==> 3种情况
  • 楼梯4 1+1+2 2+2 1+2+1 2+1+1 1+1+1+1 ==> 5种情况


我们发现每次楼梯数n和情况数的关系是: 爬上当前楼梯数的情况 = 上一层楼梯的情况+上上层楼梯的情况数


接下来我们用**动归五部曲**来解决这个Problem


定义一个一维数组dp来记录不同楼层的状态


确定dp数组以及下标的含义

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


确定递推公式

从dp[i]的定义可以看出,dp[i] 可以有两个方向推出来。


首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶不就是dp[i]了么。


还有就是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶不就是dp[i]了么。


那么dp[i]就是 dp[i - 1]与dp[i - 2]之和!即: dp[i] = dp[i - 1] + dp[i - 2]。


在推导dp[i]的时候,一定要时刻想着dp[i]的定义,否则容易跑偏。


dp数组如何初始化

由于 n >=1,所以dp初始化: dp[1] = 1,dp[2] = 2. 当然从dp[0] = 0,也是没有问题的(正好和斐波那契规律一致)


确定遍历顺序

从递推公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,遍历顺序一定是从前向后遍历的


举例推导dp数组

举例当n为5的时候,dp (dp数组)应该是这样的

5eee8eb0170fcb5fb5415d24d9764051.png


如果代码出问题了,就把dp 打印出来,看看究竟是不是和自己推导的一样。


此时大家应该发现了,这不就是斐波那契数列么!


唯一的区别是,没有讨论dp[0]应该是什么,因为dp[0]在本题没有意义!(写了对结果也没有影响)


参考代码

#include<bits/stdc++.h>
using namespace std;
int climbStairs(int n) {
  vector<int> dp(50);
  dp[1] = 1;
  dp[2] = 2;
  for(int i = 3;i <= n; i++){
    dp[i] = dp[i-1]+dp[i-2];
  }
  return dp[n];
}


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 。

思路分析


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


所以示例1中只花费一个15 就可以到阶梯顶,最后一步可以理解为 不用花费。


也就是说我们只要爬到最后一阶或者前一阶,然后最后一步就不用花费了


读完题大家应该知道指定需要动态规划的,因为爬楼梯是从前面的楼梯向上爬的,后面的情况数由前面决定.


动归五部曲


确定dp数组以及下标的含义


使用动态规划,就要有一个数组来记录状态,本题只需要一个一维数组dp[i]就可以了。


dp[i]的定义:到达第i个台阶所花费的最少体力为dp[i]。(注意这里认为是第一步一定是要花费,最后一步不需要花费)


对于dp数组的定义,大家一定要清晰!


确定递推公式

可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[i-2]。


那么究竟是选dp[i-1]还是dp[i-2]呢?


一定是选最小的,所以dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];


注意这里为什么是加cost[i],而不是cost[i-1],cost[i-2]之类的,因为题目中说了:每当你爬上一个阶梯你都要花费对应的体力值


dp数组如何初始化

根据dp数组的定义,dp数组初始化其实是比较难的,因为不可能初始化为第i台阶所花费的最少体力。


那么看一下递归公式,dp[i]由dp[i-1],dp[i-2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就够了,其他的最终都是dp[0] dp[1]推出。


所以初始化代码为:


vector<int> dp(cost.size());
dp[0] = cost[0];
dp[1] = cost[1];

确定遍历顺序


最后一步,递归公式有了,初始化有了,如何遍历呢?


因为是模拟台阶,而且dp[i]又dp[i-1]dp[i-2]推出,所以是从前到后遍历cost数组就可以了。


但是稍稍有点难度的动态规划,其遍历顺序并不容易确定下来。


例如:01背包,都知道两个for循环,一个for遍历物品嵌套一个for遍历背包容量,那么为什么不是一个for遍历背包容量嵌套一个for遍历物品呢? 以及在使用一维dp数组的时候遍历背包容量为什么要倒序呢?这些在后面将详细的介绍


举例推导dp数组


拿示例2:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] ,来模拟一下dp数组的状态变化,如下:


57aad02763b82209762983d98de40601.png

如果大家代码写出来有问题,就把dp数组打印出来,看看和如上推导的是不是一样的。


参考代码

#include<bits/stdc++.h>
using namespace std;
int minCostClimbingStairs(vector<int>& cost) {
  vector<int> dp(cost.size());//定义dp
  dp[0] = cost[0];//初始化dp(相当于第一步已经支付过了.其实第一步是不需要花费的,这也导致了,
    //我们认为第一步需要花费那么最后一步就认为不用花费.只需要走到最后一阶或者前一阶就可)
  dp[1] = cost[1];
  for(int i = 2; i < cost.size(); i++) {// 计算dp
    dp[i] = min(dp[i-1],dp[i-2]) +cost[i];//状态转移方程
  }
  return min(dp[cost.size()-1],dp[cost.size()-2]);//最后一步不需要支付费用,因为已经支付过了.
}


相关文章
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
61 6
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
53 4
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
124 2
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
40 1
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
4月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
70 7
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
54 5
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
31 4
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
Leetcode题目"剑指 Offer 21. 调整数组顺序使奇数位于偶数前面"的两种Python解决方案,一种是使用双端队列调整数组顺序,另一种是使用双指针法将奇数移到数组前半部分,偶数移到后半部分。
29 4