动态规划---斐波那契数列模型

news2024/12/25 21:50:18

目录

一、斐波那契数列的基本概念

二、动态规划在斐波那契数列中的应用与优势

三、实际案例:使用动态规划解决斐波那契数列问题

四、动态规划问题的做题步骤

五、例题

1、第N个泰波那契数---点击跳转题目

2、三步问题----点击跳转题目

3、最小花费爬楼梯----点击跳转题目

4、解码方法----点击跳转题目


本文通过对斐波那契数列问题引入动态规划,这也是动态规划问题中比较简单的一类问题,通过这个模型我们一起来学习什么是动态规划算法并且动态规划问题的基本做题步骤是什么

一、斐波那契数列的基本概念

斐波那契数列是这样一个数列:从第三项开始,每一项都等于前两项之和。数列的前几项为:0, 1, 1, 2, 3, 5, 8, 13, 21, ...。这个数列的特性使得它在很多实际问题中都有着重要的应用。

二、动态规划在斐波那契数列中的应用与优势

在计算斐波那契数列时,我们可以使用递归的方式。然而,递归方式在计算较大的斐波那契数时,会存在大量的重复计算,效率非常低。而动态规划技术可以有效地避免这种重复计算,显著提高计算效率。

动态规划的基本思想是将问题分解为若干个子问题,并将这些子问题的解保存起来,以便在计算新的子问题时能够直接利用之前的结果,避免重复计算。在斐波那契数列问题中,我们可以使用动态规划技术,从下往上计算斐波那契数列,并将每个计算结果保存起来,这样在计算较大的斐波那契数时,就可以直接使用之前保存的结果,通过空间换时间的操作,大大提高了计算效率。

三、实际案例:使用动态规划解决斐波那契数列问题

下面是一个C++代码示例,展示了如何使用动态规划来计算斐波那契数列:

#include<iostream>

const int N = 100;
int dp[N];

int main()
{
    dp[0] = 1,dp[1] = 1;
    int n;cin>>n;
    for(int i=2;i<=n;i++)
        dp[i] = dp[i-1] + dp[i-1];
    cout<<dp[n]<<endl;
    return 0;
}

在上述代码中,我们创建了一个列表dp来保存斐波那契数列的值。初始时,列表的前两个元素被设置为斐波那契数列的前两项。然后,我们通过一个循环,从第三项开始,依次计算斐波那契数列的每一项,并将结果保存在dp列表中。最后,返回dp[n],即第n项斐波那契数的值。

动态规划在斐波那契数列中的核心思想是通过保存子问题的解来避免重复计算。具体方法是将问题分解为子问题,并按照某种顺序(如上述示例中的从下往上)依次解决这些子问题,同时保存每个子问题的解。在计算新的子问题时,如果其解已经保存,则直接利用保存的解,否则通过解决更小的子问题来得到其解,并将解保存起来。

四、动态规划问题的做题步骤

在分析题目时分为五步:

  • 状态表示:根据 经验 + 题目要求 得出

dp[i]代表什么? 一般可以 以i为结尾 以i为起点 做状态表示

  • 状态转移方程:根据dp[i]的最近一步推导得出关于dp[i]的递推公式
  • 初始化:根据递推公式来初始化需要先用到的dp值
  • 填表顺序:根据递推公式来判断填表顺序
  • 返回值:题目要求

在编写代码时分为四步:

  • 创建dp表
  • 初始化
  • 填表
  • 返回值

五、例题

1、第N个泰波那契数---点击跳转题目

本题很简单明了,题目里面就已经给出了状态转移方程,由此,我们定义dp[n]是第n个泰波斐契数

代码:

class Solution {
public:
    int m = 40;
    int tribonacci(int n) 
    {
        //空间优化
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;

        int a = 0,b = 1, c = 1,d;
        for(int i=3;i<=n;i++)
        {
            d = a + b + c;
            //滚动操作
            a = b; b = c; c = d;
        }
        return d;
    }
};

由于每个数只会用到它的前三个数,所以我们可以使用滚动数组来进行空间优化

2、三步问题----点击跳转题目

分析:

  • 状态表示:以i为结尾分析,dp[i]为到第i个台阶的方法总数
  • 状态转移方程:分析最近一步,由于一次可以跨1到3个台阶,所以到第i个台阶的最近一步就是先到i-1、i-2、i-3这三个台阶,再分别跨1、2、3个台阶到达第i个台阶,那么从i-1层到i层这种方式到i层的方法数就是到i-1层的方法数,以此类推:dp[i] = dp[i-1]+dp[i-2]+dp[i-3]; 可以看出递推公式和上一题一摸一样,只不过这个需要我们根据题意和经验自己推导。
  • 初始化:把上1、2、3层的台阶方法数先初始化,因为递推公式要用到三个数,根据题意,dp[1] = 1,dp[2] = 2,dp[3] = 4;
  • 填表顺序:由于递推公式是由小推大,所以从左向右填表
  • 返回值:根据题意,返回dp[n] 对1000000007取模的结果即可

代码:

class Solution {
public:
    int waysToStep(int n) 
    {
        int MOD = 1e9 + 7;
        //创建dp表
        vector<int> dp(10+n);
        //初始化
        dp[1] = 1;dp[2] = 2;dp[3] = 4;
        //填表
        for(int i=4;i<=n;i++)
            dp[i] = ((dp[i-1]+dp[i-2])%MOD+dp[i-3])%MOD;
        //返回值
        return dp[n];
    }
};

题意是结果取模即可,但是因为数值过大,计算过程中就会爆数据范围,所以只能每做一次加法取一次模,和对结果取模等价; 注意,取模运算具有分配律:(A+B)%C = A%C + B%C

3、最小花费爬楼梯----点击跳转题目

分析:题中表示,从第i层向上爬,需要花费cost[i]的体力值,可以从0、1层向上爬,也就死选择到0/1层并不花费体力值;

  • 状态表示:以i为结尾,dp[i]:到第i层花费的最小体力值
  • 状态转移方程:由dp[i]最近一步推导,由于一次可以爬1~2步,到第i层之前,要么在i-1层,要么在i-2层,分类讨论,从i-1层到i层的最小花费为:dp[i-1]+cost[i-1];从i-2层到i层的最小花费为:dp[i-2]+cost[i-2],一共这两种情况,哪一种才是到达i层的最小花费呢?两者取个最小值即可:dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])
  • 初始化:由递推公式可知需要前两个dp值,由题意,选择上0或1层台阶不花费体力值,dp[0] = dp[1] = 0
  • 填表顺序:由递推公式是在小推大,所以我们从左向右填表
  • 返回值:根据题意,返回dp[n]就是到达第n层台阶的最小花费

代码:

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {
        int n = cost.size();
        vector<int> dp(10+n);
        dp[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];
    }
};

看到这里,可能有读者会有疑惑,分析状态表示时这三个题怎么都是以i为结尾分析,做题步骤中不是还有以i为起点分析吗?其实都可以,做题的时候觉得怎样更方便就选择怎样,那么这道题我们也以i为起点来举例分析一下

  • 状态表示:以i为起点,dp[i]表示以i为起点到楼顶的最小花费
  • 状态转移方程:分析dp[i]最近的一步,以i为起点,自然只能向后看,最近的一步就是i+1层台阶和i+2层台阶; dp[i] = cost[i] + min(dp[i+1],dp[i+2])
  • 初始化:由递推公式可知,是要先直到后面的值推导前面的值,dp[n] = 0, dp[n-1] = cost[i-1]
  • 填表顺序:由递推公式,是大推小,所以是从右往左填表
  • 返回值:根据题意,要求的是从0层或1层到n层的最小花费,所以结果为min(dp[0],dp[1])

代码:

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) 
    {   
        //dp[i]:以i位置出发到达楼顶的最小花费
        //dp[i] = min(dp[i+1]+cost[i],dp[i+2]+cost[i]);
        int n = cost.size();
        vector<int> dp(n+10);
        dp[n] = 0;dp[n-1] = cost[n-1];
        for(int i=n-2;i>=0;i--)
         dp[i] = cost[i] + min(dp[i+1],dp[i+2]);
        
        return min(dp[0],dp[1]);
    }
};

这道题怎么分析都可以,建议两种视角都看一遍加深理解

4、解码方法----点击跳转题目

分析:一个字符要么单独解码,要么合并另一个字符解码,合并时前导0情况表示解码失败

  • 状态表示:以i为结尾,dp[i]表示以i为结尾时的解码方法总数
  • 状态转移方程:根据dp[i]最近的一步,就是以i位置的字符单独解码或者i和i-1的字符合并解码两种情况,其中每种情况都要判断其合法性,是否能解码成功,解码失败则表示i位置这种情况下的解码方法数是0,如果两种情况都解码成功,dp[i] = dp[i-1] + dp[i-2]

  • 初始化:初始化0,1即可,根据是否能解码成功来分类

  • 填表顺序:从左往右
  • 返回值:dp[s.size()]

代码:

class Solution {
public:
    int numDecodings(string s) 
    {
        int n = s.size();
        vector<int> dp(10+n);
        if(s[0]-'0' != 0) dp[0] = 1;
        else dp[0] = 0;
        if(s[0]-'0' && s[1]-'0') dp[1]++;
        int t = (s[0]-'0')*10 + s[1] - '0';
        if(t>=10 && t<= 26) dp[1]++;

        for(int i=2;i<n;i++)
        {
           if(s[i] != '0') dp[i] += dp[i-1];
            int t = (s[i-1] - '0')*10 + s[i] - '0';
            if(10<=t && t<=26) dp[i] += dp[i-2];
        }

        return dp[n-1];
    }
};

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

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

相关文章

kafka启动报错(kafka.common.InconsistentClusterIdException)

文章目录 前言kafka启动报错(kafka.common.InconsistentClusterIdException)1. 查找日志2. 定位问题/解决 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不…

JVM支持的可配置参数查看和分类

JVM参数大致可以分为三类: 标注指令:-开头。 这些是所有的HotSpot都支持的参数。可以用java-help 打印出来。 非标准指令: -X开头。 这些指令通常是跟特定的HotSpot版本对应的。可以用java -X打印出来。 不稳定参数: -XX 开头。 这一类参数是跟特定HotSpot版本对应的&#x…

sql题目练习

cookie注入 解题思路和之前的整数型注入一样&#xff0c;只是比整数型注入多了一步&#xff0c;题目没有给输入框&#xff0c;提示“尝试找找cookie吧”cookie的中文翻译是曲奇&#xff0c;小甜饼的意思。cookie其实就是一些数据信息&#xff0c;类型为“小型文本文件”&#…

9、案例实战【处理百万级交易无压力】:JVM栈内存与永久代大小又该如何设置?

9.1、前文回顾 上一篇文章通过案例分析,向大家介绍了在准备上线新系统时,如何根据预估的业务量和访问量来推算系统每秒的并发量。接下来,我们将探讨如何根据这个并发量来估算每秒钟请求对内存空间的占用,进而得出整个系统运行期间的JVM内存运转模型。 在得到这个JVM内存运…

如何部署 wfs 分布式服务

说明&#xff1a; wfs是海量小文件存储系统。wfs1.x不直接支持分布式存储&#xff0c;但为了应对大规模部署和高可用需求&#xff0c;推荐采用如Nginx这样的负载均衡服务&#xff0c;通过合理的资源配置和定位策略&#xff0c;可以在逻辑上模拟出类似分布式的效果。也就是说&am…

浅谈免杀下的持久化

文章目录 前记注册表计划任务COM劫持后记reference 前记 实战中持久化的手段常用的就是加服务、添改注册表、加计划任务、劫持等&#xff0c;这里探索c/c下的维权免杀 注册表 用户级 \HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run \HKEY_CURRENT_USER…

POJO,Entity,model,domain,view,DTO,VO,Param这些分别都是什么含义?怎样理解?

目录 1. 前言 2. POJO的含义 3. entity(实体) 4. model(模型) 5. domain(域) 6. view(视图) 7. DTO(数据传输对象) 8. VO(真正视图层) 9. Param(参数) 10. 总结 1. 前言 在日常开发的过程中&#xff0c;如果我们接手一个新的项目之后&#xff0c;通常会有各种各样的…

Java基础之JVM对象内存分配机制简介

一 对象内存分配 1.1 运行时数据区域 1.2 常见java应用启动JVM参数&#xff1a; -Xss&#xff1a;每个线程的栈大小(单位kb)-Xms&#xff1a;堆的初始大小&#xff0c;默认物理内存的1/64,示例&#xff1a;-Xms:4g -Xms:10m-Xmx&#xff1a;堆的最大可用大小&#xff0c;默认物…

vue cli3开发自己的插件发布到npm

具体流程如下&#xff1a; 1、创建一个vue项目 vue create project 2、编写组件 &#xff08;1&#xff09;新建一个plugins文件夹&#xff08;可自行创建&#xff09; &#xff08;2&#xff09;新建Button组件 &#xff08;3&#xff09;组件挂载&#xff0c;为组件提供 in…

Opencv | 边缘检测 轮廓信息

目录 一. 边缘检测1. 边缘的定义2. Sobel算子 边缘提取3. Scharr算子 边缘提取4. Laplacian算子 边缘提取5. Canny 边缘检测算法5.1 计算梯度的强度及方向5.2 非极大值抑制5.3 双阈值检测5.4 抑制孤立弱边缘 二. 轮廓信息1. 获取轮廓信息2. 画轮廓 一. 边缘检测 1. 边缘的定义…

css中新型的边框设置属性border-inline

一、概念与背景 border-inline 是 CSS Logical Properties and Values 模块中的一个属性&#xff0c;用于控制元素在流内&#xff08;inline&#xff09;方向上的边框。该模块旨在提供与书写模式&#xff08;writing mode&#xff09;无关的布局和样式描述方式&#xff0c;使得…

牛客NC216 逆波兰表达式求值【中等 栈 C++/Java/Go/PHP】

题目 题目链接&#xff1a;https://www.nowcoder.com/practice/885c1db3e39040cbae5cdf59fb0e9382 核心 栈 参考答案C class Solution {public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可*** param tokens strin…

JVM (Micrometer)监控SpringBoot(AWS EKS版)

问题 怎样使用JVM (Micrometer)面板&#xff0c;监控Spring&#xff1f;这里不涉及Prometheus和Grafana&#xff0c;重点介绍与Micrometer与Springboot&#xff0c;k8s怎样集成。 pom.xml 引入依赖&#xff0c;如下&#xff1a; <properties><micrometer.version&…

Redis系列:内存淘汰策略

1 前言 通过前面的一些文章我们知道&#xff0c;Redis的各项能力是基于内存实现的&#xff0c;相对其他的持久化存储&#xff08;如MySQL、File等&#xff0c;数据持久化在磁盘上&#xff09;&#xff0c;性能会高很多&#xff0c;这也是高速缓存的一个优势。 但是问题来了&am…

论机器学习(ML)在网络安全中的重要性

机器学习是什么&#xff1f; 机器学习(ML)是人工智能的一个分支&#xff0c;它使用算法来使计算机系统能够自动地从数据和经验中进行学习&#xff0c;并改进其性能&#xff0c;而无需进行明确的编程。机器学习涉及对大量数据的分析&#xff0c;通过识别数据中的模式来做出预测…

Python | 获取PCD点云数据强度等信息

最近工作需要&#xff0c;需要获取PCD点云数据的强度等信息&#xff0c;给出open3d和pypcd两种方法获取强度信息。读取的PCD数据头格式如下&#xff1a; VERSION 0.7 FIELDS x y z intensity laserid timeoffset yawangle SIZE 4 4 4 1 2 8 4 TYPE F F F U U F F COUNT 1 1 1 …

Spring Boot | Spring Boot 实现 “Redis缓存管理“

目录 : Spring Boot 实现 "Redis缓存管理" :一、Spring Boot 支持的 "缓存组件" &#xff08; 如果 “没有” 明确指定使用自定义的 "cacheManager "或 "cacheResolver" &#xff0c;此时 SpringBoot会按照“预先定义的顺序” 启动一个…

浅谈本地缓存的几种方案选型

一、摘要 说到缓存&#xff0c;面试官基本上会绕不开以下几个话题&#xff01; 项目中哪些地方用到了缓存&#xff1f;为什么要使用缓存&#xff1f;怎么使用它的&#xff1f;引入缓存后会带来哪些问题&#xff1f; 这些问题&#xff0c;基本上是互联网公司面试时必问的一些…

STM32,复位和时钟控制

外部时钟 HSE 以后需要用到什么就这样直接拿去配就行了

【Linux网络】FTP服务

目录 一、FTP简介 1.FTP-文件传输协议 2.FTP的两种模式 二、安装配置FTP 1.安装环境 2.对文件的操作 3.切换目录 4.设置匿名用户 5.图形化界面登录 6.白名单与黑名单 重点与难点 一、FTP简介 1.FTP-文件传输协议 FTP是FileTransferProtocol&#xff08;文件传输协…