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

目录

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

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

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

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

五、例题

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.mfbz.cn/a/574096.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SparkSQL---简介及RDD V.S DataFrame V.S Dataset编程模型详解

一、SparkSQL简介 SparkSQL&#xff0c;就是Spark生态体系中的构建在SparkCore基础之上的一个基于SQL的计算模块。SparkSQL的前身不叫SparkSQL&#xff0c;而叫Shark&#xff0c;最开始的时候底层代码优化&#xff0c;sql的解析、执行引擎等等完全基于Hive&#xff0c;总之Sha…

ElasticSearch:查询操作合集

先看下我的数据&#xff1a; 1、查询所有文档&#xff1a; GET /cartest/_search或者 GET /cartest/_search {"query": {"match_all": {}} }2、匹配查询&#xff1a; match匹配类型查询&#xff0c;会把查询条件进行分词&#xff0c;然后进行查询&…

el-table 三角形提示

<template><div><el-table :data"tableData" style"width: 100%"><el-table-column prop"ddd" label"日期2" width"150" /><el-table-column prop"ddd" label"日期2" width…

Apifox接口调试工具

1、Apifox简介 Apifox 是集 API 文档、API 调试、API Mock、API 自动化测试多项实用功能为一体的 API 管理平台&#xff0c;定位为 Postman Swagger Mock JMeter。旨在通过一套系统、一份数据&#xff0c;解决多个工具之间的数据同步问题。只需在 Apifox 中定义 API 文档&a…

线性模型算法-完结总结篇

简介 该篇文章就是在CSDN上更新的最终版本。 本文章将介绍&#xff1a;机器学习中的线性模型有关内容&#xff0c;我将尽可能做到 详细地介绍线性模型的所有相关内容,模块如下&#xff0c;希望这些将有助于读者了解这种最初步但却强大的算法&#xff1a; 线性回归逻辑回归 S…

Day22 SSH远程管理服务

sshd服务&#xff0c;系统自带&#xff0c;默认开机自启运行 云/物理服务器的安全组和防火墙默认放行该端口 软件包&#xff1a;openssh-server&#xff08;服务端&#xff09;&#xff1b;openssh-client&#xff08;客户端&#xff09;&#xff1b; 格式&#xff1a;ssh I…

抖音小店没有流量怎么办?这两点做对!别人羡慕你赚的盆满钵满

哈喽~我是电商月月 电商行业&#xff0c;说一句实在的话&#xff0c;每一年都有一批人说电商不好做&#xff0c;但每一年都有人从电商行业赚到钱 做抖音小店没流量出不出单的原因其实很简单&#xff0c;就是思维不同&#xff0c;导致的结果差异 我们做抖店并不是赚一单就满足…

三维点云处理-滤波器

前言&#xff1a; 点云中往往会存在很多噪声&#xff0c;也就是常说的离群点&#xff0c;如下左图中的黑色圈位置&#xff0c;可能会对有效数据的提取分析造成影响&#xff0c;因此在数据分析前通常会考虑采用滤波器&#xff08;Filter&#xff09;等手段进行一些预处理的操作。…

东北大学工程训练CNC加工中心(坤图)

东北大学加工中心&#xff08;CNC&#xff09;采用的系统为FANUC系统。 要求学生自主设计图样&#xff0c;编写GCODE文件&#xff0c;操作电脑使机床按设计路径铣出图案。 本人设计的图样为坤坤图 图为用CAD设计绘制的图样。 计算坐标&#xff0c;设计铣刀轨迹&#xff0c;得…

解析社交电商:从私域流量到移动突破口

亲爱的朋友们&#xff0c;我是微三云的周丽&#xff0c;一名专注于私域电商模式创新的探索者。 随着互联网的迅速发展和科技的不断进步&#xff0c;社交电商作为新型商业模式不断崛起。在这个时代&#xff0c;私域流量、社群电商、社区电商以及移动电商等概念层出不穷&#xf…

成功密码期刊投稿简介

《成功密码》综合版是由国家新闻出版总署批准&#xff0c;江西省教育厅主管的正规期刊&#xff0c;"以培养担当民族复兴大任的时代新人为着眼点&#xff0c;强化教育引导、实践养成、制度保障"&#xff0c;倡导教育研究的学术水准&#xff0c;注重理论与实践的有机结…

Linux消息队列信号量(了解)

消息队列 要实现进程间通信我们必须得让不同的进程看到同一份资源&#xff0c; 根据这个资源的不同&#xff08;文件缓冲区&#xff0c; 内存块&#xff0c; 队列&#xff09; 我们将通信方式分为管道&#xff0c;共享内存&#xff0c;以及我们接下来要讲的消息队列。 消息队…

【学习笔记二十七】EWM存储类型控制

一、EWM存储类型控制概述 Storage control 是用来决定仓库产品移动时所需要的流程步骤。它的目的是用来处理基于仓库物理布局及仓库流程所要求的复杂的上架和下架流程步骤。 仓库里常见的操作步骤有:Picking、Packing、Staging、Loading、Putaway、Unloading、Counting、Quali…

【C语言】联合体详解

目录 1.联合体的声明 2.联合体的特点 3.相同成员的结构体和联合体对比 4.联合体大小的计算 1.联合体的声明 像结构体一样&#xff0c;联合体也是由一个或者多个成员构成&#xff0c;这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。 联合体的特点是所…

操作系统:进程间通信 | System V IPC

目录 前言&#xff1a; 1.共享内存 1.1.什么是共享内存 1.2.共享内存使用接口 shmget函数 shmat函数 shmdt函数 shmctl函数 2.共享内存实现通信 2.1.代码实现 comm.hpp server,cpp client.cpp 2.2.共享内存的缺点 2.3.实现通信的同步化 2.4共享内存通信的优势 3.…

Vitis HLS 学习笔记--HLS入门示例集合-目录

目录 1. 示例集合概述 2. Interface 接口 2.1 Aggregation_Disaggregation 聚合与解聚 2.1.1 aggregation_of_m_axi_ports 2.1.2 aggregation_of_nested_structs 2.1.3 aggregation_of_struct 2.1.4 auto_disaggregation_of_struct 2.1.5 disaggregation_of_axis_port …

游戏工作室为什么要使用海外住宅IP防封?

当谈到游戏工作室时&#xff0c;它们通常以多开游戏账号来获取收益为主要目标。这种商业模式在游戏产业中已经成为一个独特而且颇具潜力的领域。然而&#xff0c;随之而来的是防封问题&#xff0c;特别是当游戏工作室试图通过多开账号来赚取更多收益时。因此&#xff0c;我们有…

Navicat连接SQLSever报错:[08001] MicrosoftTCP Provider 远程主机强迫关闭了一个现有的连接

Navicat连接SQLSever报错&#xff1a;[08001] [Microsoft][SQL Server Native Client 10.0]TCP Provider: 远程主机强迫关闭了一个现有的连接 问题分析 旧版的MSSQL 如果不是最新版的&#xff0c;可以去这安装以下即可。 最新版的MSSQL 如果是安装最新版的MSSQL连接不上很正…

Kubernetes 的未来:通过生成式 AI 实现的潜在改进

Kubernetes 是一个用于自动化部署、扩展和管理容器化应用程序的开源平台&#xff0c;它彻底改变了 IT 行业。然而&#xff0c;与所有创新技术一样&#xff0c;它不断寻求改进以提高效率、可用性和功能。生成式人工智能&#xff08;Generative AI&#xff09;是一个有望取得改进…

C++:匿名对象

在C中&#xff0c;匿名对象是指在不分配给定变量名称的情况下创建的临时对象。这些对象通常用于传递参数给函数、作为函数的返回值或者在表达式中使用。 创建匿名对象 在C中&#xff0c;您可以使用类的构造函数来创建匿名对象。例如&#xff1a; MyClass(); // 创建一个匿名…
最新文章