DAY42:动态规划(二)斐波那契数列+爬楼梯+最小花费爬楼梯

news2024/9/25 23:12:11

文章目录

    • 509.斐波那契数列
      • 思路:动规五步
        • 确定dp数组和数组下标含义
        • 递推公式
        • DP数组初始化
        • 遍历顺序
        • 打印DP数组
      • 完整版
        • debug测试
      • 空间复杂度优化版
        • 优化思路
    • 70.爬楼梯
      • 思路
        • DP数组的含义以及下标含义
        • 递推公式
        • DP数组初始化
        • 遍历顺序
        • 打印DP数组
      • 完整版
        • debug测试
      • 空间复杂度优化写法
    • 746.使用最小花费爬楼梯
      • 思路
        • DP数组含义
        • 递推公式
        • DP数组初始化
        • 遍历顺序
          • 遍历顺序补充
        • 打印DP数组
      • 完整版
      • 空间复杂度优化写法

509.斐波那契数列

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

F(0) = 0F(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

提示:

  • 0 <= n <= 30

思路:动规五步

确定dp数组和数组下标含义

DP题目都需要定义一维或者二维的状态转移数组,通常是叫dp。

本题中,dp[i]表示第i个斐波那契数的数值为dp[i]

递推公式

本题是比较简单的DP题目,就是因为题目描述中已经把递推公式告诉我们了。

递推公式:dp[i]=dp[i-1]+dp[i-2]

DP数组初始化

题目描述已经说了,dp[0]=1

遍历顺序

因为dp[i]是由dp[i-1]和dp[i-2]得到的,因此需要从前往后遍历,才能保证每次dp[i]能够考虑到前面的两个元素。

打印DP数组

这一步主要用于debug,打印出来看看和想象的是否一样

完整版

class Solution {
public:
    int fib(int n) {
        if(n==0) 
            return 0;
        if(n==1) 
            return 1;
        //建立dp数组
        vector<int>dp(n+1);
        //dp数组初始化,初始化依赖于递推公式
        //注意这里初始化需要放到if特殊情况后面,因为如果n是0,就不存在dp[1]
        dp[0]=0;
        dp[1]=1;
        
        //递推公式
        for(int i=2;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

debug测试

在这里插入图片描述
这段代码的问题出在没有处理 n 为 0 或 1 的情况。如果 n 为 0,那么 dp[1] 就不存在,这时试图访问 dp[1] 会导致溢出

dp[0]=0;dp[1]=1;if特殊情况需要放最前面,因为如果n是0,就不存在dp[1]

修改加上if条件之后通过。

空间复杂度优化版

  • 实际上,我们只需要维护两个数值就可以了,不需要记录整个序列。
class Solution {
public:
    int fib(int N) {
        if (N <= 1) return N;
        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];
    }
};

优化思路

斐波那契数列的定义:F(0) = 0,F(1) = 1,F(n) = F(n-1) + F(n-2) 对于所有 n >= 2。这意味着,要计算第 n 个斐波那契数,只需要知道前两个斐波那契数,即 F(n-1) 和 F(n-2)

优化版本的斐波那契数列计算利用了这个性质。在循环开始时,dp[0] 和 dp[1] 分别存储 F(n-2) 和 F(n-1)。然后,我们计算新的斐波那契数 F(n) = dp[0] + dp[1],并更新 dp[0] 和 dp[1],以备下一个循环使用。所以,我们只需要两个变量就可以计算出斐波那契数列的下一个值,而不必维护整个数列。

这样的优化实际上是一个空间优化,称为 “滚动数组” 或者 “滑动窗口” 的策略。其基本思想是只保存当前阶段需要的数据,淘汰过去不再需要的数据,避免存储不必要的信息,从而降低空间复杂度。

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

70.爬楼梯

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

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

示例 1:

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

示例 2:

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

1. 1+ 1+ 12. 1+ 23. 2+ 1

提示:

  • 1 <= n <= 45

思路

一共n阶台阶,1阶:1步,2阶:2种(2或者1+1),3阶:3种(2+1或1+2或1+1+1)4阶:5种。

我们可以发现,3阶,只能从1阶和2阶迈上来,实际上就是1阶的1种方法加上2阶的2种方法

4阶,只能从2阶和3阶迈上来,因此登上4阶的方法数就是登上2阶的方法数+登上3阶的方法数,2+3=5种。

我们此时就可以发现递推关系,也就是当前阶梯的状态,依赖于他的前两个阶梯的状态。(一次性最多迈两步)

也就是说,因为每次只能爬 1 级或 2级,所以f(x)的数值只能从f(x-1)和f(x-2)转移过来。而这里要统计方案总数,我们就需要对这两项的贡献求和

DP数组的含义以及下标含义

dp[i]:达到第i阶,有dp[i]种方法。

后面的推导都是基于含义

递推公式

dp[i]=dp[i-1]+dp[i-2],其中dp[i-1]表示达到第i-1阶有多少种方法,dp[i-2]同理

DP数组初始化

首先因为题目描述达到第一阶有1种方法第二阶有2种,所以dp[1]=1,dp[2]=2.

dp[0]的含义是,达到第0阶需要多少种方法。但是本题中,dp[0]是没有意义的!因为题目给出的数据范围,n是一个>=1的正整数,因此我们完全不需要考虑dp[0]的情况,也不需要像题解一样令dp[0]=1,因为没有意义。

遍历顺序

遍历顺序一定是从前往后,因为本题也属于斐波那契数列题目,当前值基于他的前两个状态

打印DP数组

我们可以先推导自己认为的DP数组数值,然后打印看是否符合要求。

在这里插入图片描述

完整版

  • 本题也是一道斐波那契数列的相关题目
class Solution {
public:
    int climbStairs(int n) {
        if(n<=2) return n;
        vector<int>dp(n+1,0);
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};

debug测试

Line 1034: Char 34: runtime error: applying non-zero offset 4 to null pointer (stl_vector.h)

在这里插入图片描述
这个错误信息是说试图对一个空的vector应用非零的偏移量。这个问题出在使用 dp[i] 之前没有为 dp 分配足够的空间

在C++中, std::vector 的初始大小为0,如果试图访问或修改不存在的元素(如 dp[1]dp[2]),这就会导致运行时错误

需要先调用 std::vector::resize 或者在创建 std::vector 时就指定它的大小,才能保证有足够的空间来存储元素。

修改dp初始化:vector<int>dp(n+1,0)

空间复杂度优化写法

  • 很多动规的题目其实都是当前状态依赖前两个,或者前三个状态,都可以做空间上的优化但面试中能写出版本一就够了,清晰明了,如果面试官要求进一步优化空间的话,我们再去优化
  • 因为版本一才能体现出动规的思想精髓,递推的状态变化、
// 版本二
class Solution {
public:
    int climbStairs(int n) {
        if (n <= 1) return n;
        int dp[3];
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n; i++) {
            int sum = dp[1] + dp[2];
            dp[1] = dp[2];
            dp[2] = sum;
        }
        return dp[2];
    }
};

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

提示:

  • 2 <= cost.length <= 1000
  • 0 <= cost[i] <= 999

思路

本题首先要明确题意。题目中没有给出楼顶的位置,但是楼顶的阶数应该是cost.size()而不是cost数组的最大下标。题意如下图所示。

在这里插入图片描述

可以选择0或者1往上跳,每次往上跳都花费cost[i]的体力。

DP数组含义

我们要求的是到达楼顶的最小花费,dp[i]表示的就是花费。

i表示的是当前到了哪个台阶,而dp[i]的值表示的就是到i位置时候的所有花费

DP数组含义一定要搞清楚,这一点很重要,递推公式基于数组

递推公式

递推公式我们需要得到的是dp[i]。本题可以一步一个台阶或者一步两个台阶,因此,dp[i]是由dp[i-1]或者dp[i-2]跳上来的

dp[i]表示的是,跳到i位置所需要的最小花费。因为既可以从i-1跳上来,也可以从i-2,因此递推就是取这二者花费的最小值。公式推导如下图:

在这里插入图片描述
因此公式为,dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])

DP数组初始化

dp公式可以看出,最开始的dp[2]是由dp[1]和dp[0]求得。也就是说我们只需要初始化dp[1]和dp[0]

因为0和1是初始值,往上跳的时候才需要花费体力值,因此dp[0]和dp[1]的值都是0.

(DP数组的含义:dp[i]表示的是跳到i时候的花费,初始值花费就是0)

遍历顺序

本题也是爬楼梯的衍生题目,因此也是从前到后遍历。

遍历顺序补充

但是稍稍有点难度的动态规划,其遍历顺序并不容易确定下来。 例如:01背包,都知道两个for循环,一个for遍历物品,嵌套一个for遍历背包容量,那么,为什么不是一个for遍历背包容量,嵌套一个for遍历物品呢? 以及在使用一维dp数组的时候遍历背包容量为什么要倒序呢

这些问题都是和遍历顺序有关的,等学到了背包再进行对比。

打印DP数组

debug过程中如果出现问题,就把预期DP数组写出,再打印进行对比。

预期DP数组:

在这里插入图片描述

完整版

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        if(cost.size()<=1) return 0;
        int n=cost.size();
        //初始化
        vector<int>dp(n+1,0);
        //dp[0]=0;已经进行了0的初始化这两句可以不写
        //dp[1]=0;
        for(int i=2;i<=n;i++){
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[n];
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

空间复杂度优化写法

// 版本二
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int dp0 = 0;
        int dp1 = 0;
        for (int i = 2; i <= cost.size(); i++) {
            int dpi = min(dp1 + cost[i - 1], dp0 + cost[i - 2]);
            dp0 = dp1; // 记录一下前两位
            dp1 = dpi;
        }
        return dp1;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
  • 在面试中,能写出版本一就行,除非面试官额外要求 空间复杂度,那么再去思考版本二,因为版本二还是有点绕。版本一才是正常思路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/738515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10.24UEC++、事件

1. 类内部声明事件类型&#xff1a; 生成actor 绑定到事件上 实现&#xff1a; 移除&#xff1a;

使用docker安装redis并使用redis deskTop manager连接

使用docker安装redis 提前准备环境 linux、 docker环境 # 使用命令查看docker环境是否正常docker images(任何一个docker命令就可以&#xff0c;不一定必须这个) 下载redis镜像 # 使用命令下载镜像文件docker pull redis 确认镜像 # 使用命令查看镜像docker images使用redis d…

功能测试的测试流程是什么样的?

概述 本流程是描述软件功能自动化测试过程中的步骤、内容与方法&#xff0c;明确各阶段的职责、活动与产出物。 流程活动图 活动说明 测试计划&#xff08;可选&#xff09; 与以前的测试计划过程一致&#xff0c;只是在原来的测试计划中&#xff0c;添加对项目实施自动化测试…

Qt窗体全屏与复原,子窗体全屏与复原

QT QWidget窗体全屏时&#xff0c;全屏与最大化的区别时最大化有标题框全屏没有框&#xff0c;可以使用自带的函数&#xff1a; showFullScreen(); 复原时也有专门的函数&#xff1a; showNormal(); 如果我想在点击最大化按钮时&#xff0c;让窗体全屏。这个时候最好有线程…

学好工作汇报,你离升职加薪也就不远了

在职场中&#xff0c;工作汇报是展示你的价值和成果的重要环节。无论你是想要升职还是加薪&#xff0c;一个出色的工作汇报能够给你带来巨大的竞争优势。然而&#xff0c;许多人对于如何进行有效的工作汇报感到困惑。在本文中&#xff0c;我们将向您介绍一些关键技巧和策略&…

路径规划算法:基于骑手优化优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于骑手优化优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于骑手优化优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

Linux学习之vim在光标所在处写入内容,保存退出

vim insertTest使用vim打开一个叫做insertTest新文件。 输入命令之后&#xff0c;按下Enter&#xff08;回车键&#xff09;&#xff0c;就可以进入下边的操作页面。 按i在当前光标位置处&#xff0c;就可以插入内容了。 接下来写入内容跟记事本里边是差不多的。 按一下…

【Spring】Spring基础知识 Java开发必看

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前专攻C/C、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL、蓝桥杯&am…

vue拼接html中onclick的触发方式,模版字符串拼接点击事件在vue项目中不生效问题

模版字符串拼接点击事件在vue项目中不生效问题 下面的点击事件没有任何效果&#xff0c;但是如果换成onclick绑定事件则会提示没有该方法。主要原因是&#xff1a; 模版字符串中拼接的html片段中的方法调不到vue中this.methods里的东西&#xff0c;因为methods里的代码是编译…

物体识别(香蕉)

✨✨✨ 感谢优秀的你打开了小白的文章 “希望在看文章的你今天又进步了一点点&#xff0c;生活更加美好&#xff01;”&#x1f308;&#x1f308;&#x1f308; 目录 1.数据集下载 2.数据集导入 3.结果 1.数据集下载 包含所有图像和CSV标签文件的香蕉检测数据集可以直接从互…

day1-二分查找

二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4 解…

单片机基于stm32单片机的数字温度计设计_kaic

摘 要 古往今来,陶瓷在我们的生活中一直都是不可或缺的物品,而随着当今社会经济的快速发展,人们对于这些高档陶瓷产品的使用性能和产品质量上的要求也愈加严格。那么在陶瓷品的生产过程中,想要提高陶瓷品的品质和合格率,能够随时监测温度的温度计是必不可少的。 本课题的研究是…

什么是IP地址定位技术

IP地址定位是一种技术手段&#xff0c;可以通过获取设备的IP地址精确定位设备的地理位置。它在现代社会的许多方面都有广泛的应用&#xff0c;包括网络安全、广告投放市场调研等。 在互联网时代&#xff0c;人们越来越依赖于网络而IP地址作为网络连接的基石扮演着重要的角色。…

什么软件支持快速批量处理大量视频素材呢

在视频剪辑的过程中&#xff0c;有时候为了满足视频时长的需求&#xff0c;或者为了实现视频与音频的同步操作&#xff0c;我们可能需要对视频的播放速度进行调整。如果需要处理大量的视频文件&#xff0c;传统的手动一个个调整的方法就显得有点落后了&#xff0c;今天小编要与…

JDBC事务特性

1、 事务的四种特性&#xff08;ACID&#xff09; 原子性&#xff08;Atomicity&#xff09;&#xff1a;原子性是指事务是一个不可分割的执行单元或者叫工作单位&#xff0c;事务中的操作&#xff0c;要么都发生&#xff0c;要么都不发生。 一致性&#xff08;Consistency&am…

centos7.9 安装redis5.0.7记录

参考自己之前写的安装5.0.3 centos7安装redis5.0.3详细步骤_lanren312的博客-CSDN博客 一开始大意了&#xff0c;没有安装gcc&#xff0c;直接就make [rootlocalhost redis-5.0.7]# make cd src && make all make[1]: Entering directory /opt/redis-5.0.7/srcLINK re…

Docker 多架构镜像介绍和最佳实践

Docker 多架构镜像介绍和最佳实践> 更多云原生相关技术分享请关注公众号&#xff1a;CloudNativeOps &#xff1b; 添加微信 GoOps888&#xff0c;备注 ‘加群’ 可加入云原生技术交流群。 一、多架构镜像介绍 在 Docker 中&#xff0c;同一个 Docker 镜像可以在不同的平台…

rosserial协议通信--向ros写调用过程和读取数据调用过程

rosserial通信向ros写调用过程如下&#xff1a; 在32单片机中&#xff0c;发布者调用publish函数&#xff0c;然后依次逆向&#xff08;上图是分析过程&#xff1b;调用过程逆向&#xff09;查看上图&#xff0c; 最终操作串口&#xff0c;向上位机发布数据进行通信。 rosser…

代码随想录第24天 | ● 93.复原IP地址 ● 78.子集 ● 90.子集II

93.复原IP地址 /*** param {string} s* return {string[]}*/ let road [];let path [];var restoreIpAddresses function (s) {road [];if (s.length > 12 || s.length < 4) return [];//开始就判断&#xff0c;长度大于12的字符窜或者小于4的都不行backtracking(s,…

漏洞复现 || Apache RocketMQ 远程代码执行

技术文章仅供参考&#xff0c;任何个人和组织使用网络应当遵守宪法法律&#xff0c;遵守公共秩序&#xff0c;尊重社会公德&#xff0c;不得利用网络从事危害国家安全、荣誉和利益&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的…