【动态规划算法】斐波那契数列模型

一. (1137.)第N个泰波那契数(力扣)

在这里插入图片描述

1.1动态规划的算法流程

对于初学者来讲学术上的概念晦涩难懂,将用通俗易懂的方式带来感性的理解.

1.状态表示

dp表(一维或二维数组)里面的值所表示的含义
从哪获取?
1.题目要求,如本题
2.题目没有明确说明的情况下+做题经验的累积
3.分析问题的过程中,发现重复的子问题来概括

2.状态转移方程

就是dp表元素等于什么,是一种递推关系式,本题已告知

3.初始化

要确保填dp表时不越界,对填表时越界的元素首先初始化

4.填表顺序

为了填写当前状态的时候,所需要的状态已经计算过了的顺序,本题为从左向右

5.返回值

取决于题目要求+状态表示,本题为第n个数的值

class Solution {
public:int tribonacci(int n) {//处理边界情况if(n==0) return 0;if(n==1||n==2) return 1;vector<int> dp(n+1);dp[0]=0,dp[1]=1,dp[2]=1;for(int i=3;i<=n;i++){dp[i]=dp[i-1]+dp[i-2]+dp[i-3];}return dp[n];}
};

6.空间优化

只会在本系列和背包问题中出现并分析,为了降低空间复杂度,能将一个级别
分析本题:滚动数组法
常规做法通过创建一个dp表数组来计算并存储结果,但是通过递推关系式得知想得到当前数的值只需要知道前面三个元素的值即可,那么另外不需要的空间就相当于浪费,所以可以考虑用四个变量来记录,每次更新后三个变量来进行下一次计算,如此循环往复
在这里插入图片描述
要注意更新变量的数据顺序问题,避免被覆盖

做法1:
class Solution {
public:int tribonacci(int n) {//处理边界情况if(n==0) return 0;if(n==1||n==2) return 1;//优化int k=3;//初始计算次数,从第三次开始计算int a=0,b=1,c=1,d=0;while(k<=n)//判断计算次数,返回条件{d=a+b+c;//更新变量a=b,b=c,c=d;k++;}return d;}
};做法2(不用控制计算次数):
class Solution {
public:int tribonacci(int n) {if(n == 0) return 0;if(n == 1 || n == 2) return 1;int a = 0, b = 1, c = 1, d = 0;for(int i = 3; i <= n; i++){d = a + b + c;a = b; b = c; c = d;}return d;}};

二. ⾯试题 08.01. 三步问题(力扣)

在这里插入图片描述

2.1算法原理

  1. 状态表⽰

这道题可以根据「经验 + 题⽬要求」直接定义出状态表⽰:
dp[i] 表⽰:到达 i 位置时,⼀共有多少种⽅法。

  1. 状态转移⽅程

以 i 位置状态的最近的⼀步,来分情况讨论:
如果 dp[i] 表⽰⼩孩上第 i 阶楼梯的所有⽅式,那么它应该等于所有上⼀步的⽅式之和:(一次最多上三级台阶)
i. 上⼀步上⼀级台阶, dp[i] += dp[i - 1] ;
ii. 上⼀步上两级台阶, dp[i] += dp[i - 2] ;
iii. 上⼀步上三级台阶, dp[i] += dp[i - 3] ;
综上所述, dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
需要注意的是,这道题⽬说,由于结果可能很⼤,需要对结果取模。在计算的时候,三个值全部加起来再取模,即 (dp[i - 1] + dp[i - 2] + dp[i - 3]) %MOD 是不可取的, n 取题⽬范围内最⼤值时,⽹站会报错 signed integer overflow 。
对于这类需要取模的问题,我们每计算⼀次(两个数相加/乘等),都需要取⼀次模。否则,万⼀发⽣了溢出,答案就错了

  1. 初始化

从我们的递推公式可以看出, dp[i] 在 i = 0, i = 1 以及 i = 2 的时候是没有办法进⾏推导的,因为 dp[-3] dp[-2] 或 dp[-1] 不是⼀个有效的数据。因此我们需要在填表之前,将 0,1, 2位置的值初始化。根据题意,, dp[0] = 1, dp[1] = 1, dp[2] = 2。

  1. 填表顺序

爬台阶从下往上,对应数组从左往右,毫⽆疑问是「从左往右」。

  1. 返回值

应该返回 dp[n] 的值。

class Solution {
public:int waysToStep(int n) {//判断边界情况if(n==0|n==1) return 1;if(n==2) return 2;vector<int> dp(n+1);//初始化dp[0]=1,dp[1]=1,dp[2]=2;for(int i=3;i<=n;i++){//取模操作防止数据溢出dp[i]=((dp[i-1]+dp[i-2])%1000000007+dp[i-3])%1000000007;}return dp[n];}
};

三. (746.) 使⽤最⼩花费爬楼梯(力扣)

在这里插入图片描述
注意:数组内的每⼀个下标 [0, n - 1] 表⽰的都是楼层,⽽顶楼的位置其实是在 n 的位置!

3.1算法原理

解法1

1.状态表示

dp[i] 表⽰:到达 i 位置时的最⼩花费。(注意:到达 i 位置的时候, i 位置的钱不需要算上)

2.状态转移方程

根据最近的⼀步,分情况讨论:
▪ 先到达 i - 1 的位置,然后⽀付 cost[i - 1] ,接下来⾛⼀步⾛到 i 位置:dp[i - 1] + csot[i - 1]
▪ 先到达 i - 2 的位置,然后⽀付 cost[i - 2] ,接下来⾛⼀步⾛到 i 位置:dp[i - 2] + csot[i - 2]

  1. 初始化:

从递推公式可以看出,需要先初始化 i = 0 ,以及 i = 1 位置的值。容易得到dp[0] = dp[1] = 0 ,因为不需要任何花费,就可以直接站在第 0 层和第 1 层上

  1. 填表顺序:

根据「状态转移⽅程」可得,遍历的顺序是「从左往右」

  1. 返回值:

根据「状态表⽰以及题⽬要求」,需要返回 dp[n] 位置的值。

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

解法2

  1. 状态表⽰:

dp[i] 表⽰:从 i 位置出发,到达楼顶,此时的最⼩花费。

  1. 状态转移⽅程:

根据最近的⼀步,分情况讨论:
▪ ⽀付 cost[i] ,往后⾛⼀步,接下来从 i + 1 的位置出发到终点: dp[i + 1] + cost[i]
▪ ⽀付 cost[i] ,往后⾛两步,接下来从 i + 2 的位置出发到终点: dp[i + 2] + cost[i]
我们要的是最⼩花费,因此 dp[i] = min(dp[i + 1], dp[i + 2]) + cost[i] 。

  1. 初始化:

为了保证填表的时候不越界,我们需要初始化最后两个位置的值,结合状态表⽰易得: dp[n - 1] = cost[n - 1], dp[n - 2] = cost[n - 2]

  1. 填表顺序:

根据「状态转移⽅程」可得,遍历的顺序是「从右往左」。

  1. 返回值:

根据「状态表⽰以及题⽬要求」,需要返回 dp[n] 位置的值。注意,此时不需要多开辟一个空间,因为根据状态表示从n位置到n位置原地踏步的最小花费为0

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n=cost.size();vector<int> dp(n);//初始化dp[n-1]=cost[n-1];dp[n-2]=cost[n-2];for(int i=n-3;i>=0;i--)//从右往左初始化{dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i]);}return min(dp[0],dp[1]);}
};

注意状态表示可以是多个的,如果能推得状态转移方程并且最后运行通过,证明该种状态表示是正确的

四. (91.) 解码⽅法(力扣)

4.1算法原理

  1. 状态表⽰:

根据以往的经验,对于⼤多数线性 dp ,经验上都是「以某个位置结束或者开始」做⽂章,这⾥继续尝试「⽤ i 位置为结尾」结合「题⽬要求」来定义状态表⽰。dp[i] 表⽰:字符串中 [0,i] 区间上,⼀共有多少种编码⽅法。

2.状态转移方程:

关于 i 位置的编码状况,我们可以分为下⾯两种情况:
i. 让 i 位置上的数单独解码成⼀个字⺟;
ii. 让 i 位置上的数与 i - 1 位置上的数结合,解码成⼀个字⺟
在这里插入图片描述
由状态定义不用考虑i+1位置的编码,i和i-1位置的解码都有成功和失败两种可能。
解码成功:
解码方法等于前一位区间上的解码方法,相当于前一位区间上所由解码结果后面再加上当前位置解码后的字母即可
解码失败:
说明当前位置上不能单独解码或结合解码,之前的努力都白费了

3.初始化

由于可能要⽤到 i - 1 以及 i - 2 位置上的 dp 值,因此要先初始化「前两个位置」。
初始化 dp[0] :结果有0,1
i. 当 s[0] == ‘0’ 时,没有编码⽅法,结果 dp[0] = 0 ;
ii. 当 s[0] != ‘0’ 时,能编码成功, dp[0] = 1
初始化 dp[1] :结果有0,1,2
i. 当 s[1] 在 [1,9] 之间时,能单独编码,此时 dp[1] += dp[0] (原因同上,
dp[1] 默认为 0 )
ii. 当 s[0] 与 s[1] 结合后的数在 [10, 26] 之间时,说明在前两个字符中,⼜有⼀种
编码⽅式,此时 dp[1] += 1

  1. 填表顺序:

「从左往右」

  1. 返回值:

应该返回 dp[n - 1] 的值,表⽰在 [0, n - 1] 区间上的编码⽅法。

class Solution {
public:int numDecodings(string s) {int n=s.size();vector<int> dp(n);//初始化if(s[0]!='0') dp[0]=1;//处理边界情况if(n==1) return dp[0];if(s[0]!='0'&&s[1]!='0') dp[1]=1;//注意条件判断时不能连续比较int t=(s[0]-'0')*10+(s[1]-'0');if(10<=t&&t<=26) dp[1]+=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];}
};

可以看出上述代码初始化部分和循环部分代码有些重复,特别是初始化dp[1]位置时有三种情况需要判断,可以优化

4.2细节优化

dp表中多开辟一位,将dp[0]设置为辅助节点来初始化,这样原dp表中dp[1]位置初始化的复杂条件判断就可以避免,放到循环中去完成赋值,简化了一次操作
在这里插入图片描述

注意:
1.辅助节点中的值要保证后面的填表是正确的

已知会先初始化两个节点,一个为辅助节点(新dp表中dp[0]),另一个为旧dp表中的dp[0]新dp表中的dp[1]。填表前会先判断解码,单独解码时与前一个节点有关与辅助节点无关,但与前一节点共同解码时成功后会加上前前节点的解码方法数,所以此时与辅助节点有关,该情况下辅助节点应设为1而不是0

2.下标的映射关系

由于dp表中多增一位,在查找原表进行解码时下标位置要-1才能相对应

class Solution {
public:int numDecodings(string s) {int n=s.size();vector<int> dp(n+1);//初始化dp[0]=1;// 保证后续填表是正确的dp[1] = s[0] != '0';//填表for(int i=2;i<=n;i++){//解码当前位置,单独if(s[i-1]!='0') dp[i]+=dp[i-1];//与上一位置共同解码int t=(s[i-2]-'0')*10+(s[i-1]-'0');//改变下标映射关系if(10<=t&&t<=26) dp[i]+=dp[i-2];}return dp[n];}
};

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

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

相关文章

Odoo 18 PWA 全面掌握:从架构、实现到高级定制

本文旨在对 Odoo 18 中的渐进式网络应用&#xff08;Progressive Web App, PWA&#xff09;技术进行一次全面而深入的剖析。本文的目标读者为 Odoo 技术顾问、高级开发人员及解决方案架构师&#xff0c;旨在提供一份权威的技术参考&#xff0c;以指导 PWA 相关的实施项目与战略…

Binary Classifier Optimization for Large Language Model Alignment

2025.acl-long.93.pdfhttps://aclanthology.org/2025.acl-long.93.pdf 1. 概述 在生产环境中部署大型语言模型(LLMs)时,对齐LLMs一直是一个关键因素,因为预训练的LLMs容易产生不良输出。Ouyang等人(2022)引入了基于人类反馈的强化学习(RLHF),该方法涉及基于单个提示的…

在CentOS上以源码编译的方式安装PostgreSQL

下载目录&#xff1a;PostgreSQL: File Browser&#xff0c;我使用的PostgreSQLv17.5。Linux系统&#xff1a;CentOS Linux release 7.9.2009 (Core) 安装依赖包和工具链&#xff08;必须且重要&#xff01;&#xff09; yum groupinstall "Development Tools" -y yu…

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现沙滩小人检测识别(C#代码UI界面版)

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现沙滩小人检测识别&#xff08;C#代码UI界面版&#xff09;工业相机使用YoloV8模型实现沙滩小人检测识别工业相机通过YoloV8模型实现沙滩小人检测识别的技术背景在相机SDK中获取图像转换图像的代码分析工业相机图像转换…

Ubuntu服务器安装与运维手册——操作纯享版

本手册汇总了从硬件预配置、Ubuntu 安装、网络与服务配置&#xff0c;到 Windows/macOS 访问共享、MySQL 初始化的完整流程&#xff0c;便于今后运维参考。 目录 环境与硬件概览BIOS/UEFI 设置制作与启动安装介质Ubuntu 24.04 LTS 安装流程静态 IP 配置&#xff08;netplan&am…

【Nginx】Nginx进阶指南:解锁代理与负载均衡的多样玩法

在Web服务的世界里&#xff0c;Nginx就像是一位多面手&#xff0c;它不仅能作为高性能的Web服务器&#xff0c;还能轻松胜任代理服务器、负载均衡器等多种角色。今天&#xff0c;我们就来深入探索Nginx的几个常见应用场景&#xff0c;通过实际案例和关键配置解析&#xff0c;带…

原创-锐能微82xx系列电能计量芯片软件驱动开发与精度校准流程完全指南

引言 电能计量芯片的软件驱动开发是整个计量系统的核心&#xff0c;它直接决定了计量精度、系统稳定性和功能完整性。锐能微82xx系列电能计量芯片凭借其强大的数字信号处理能力和丰富的功能特性&#xff0c;为开发者提供了灵活的软件开发平台。本文将详细介绍82xx系列芯片的软…

如何使用 Apache Ignite 作为 Spring 框架的缓存(Spring Cache)后端

这份文档是关于 如何使用 Apache Ignite 作为 Spring 框架的缓存&#xff08;Spring Cache&#xff09;后端&#xff0c;实现方法级别的缓存功能。 这和前面我们讲的 Spring Data Ignite 是两个不同的概念。我们先明确区别&#xff0c;再深入理解。&#x1f501; 一、核心区别…

Android 超大图片、长图分割加载

在Android开发中&#xff0c;处理大图片的加载是一个常见且重要的问题&#xff0c;尤其是在需要显示高分辨率图片时。大图片如果不正确处理&#xff0c;可能会导致内存溢出或应用性能下降。下面是一些常用的策略和技术来优化大图片的加载&#xff1a;1. 使用图片压缩库a. Glide…

Linux:理解操作系统

文章目录数据流动操作系统数据流动 软件运行&#xff0c;必须先加载到内存&#xff0c;本质要把磁盘上的文件 加载到内存。 我们写的算法是处理存储器里面的数据&#xff0c;数据就是文件&#xff0c;我们自己写的可执行文件。 图中QQ就是软件&#xff0c;加载内存后进行下一步…

【每日一错】PostgreSQL的WAL默认段大小

文章目录题目扩展学习WAL工作原理流程图题目 扩展学习 WAL&#xff08;Write Ahead Log&#xff09;预写日志&#xff1a; WAL是PostgreSQL先写日志、后写数据的机制&#xff0c;用来防止数据丢失、提升数据恢复能力。 流程&#xff1a; 事务先写日志文件&#xff08;WAL&…

Visual Studio Code 使用指南 (2025年版)

Visual Studio Code (VS Code) 是一款由微软开发的免费、开源、跨平台的现代化轻量级代码编辑器&#xff0c;凭借其强大的核心功能、丰富的扩展生态系统以及高度可定制性&#xff0c;已成为全球数百万开发者的首选工具。本指南旨在帮助您快速上手 VS Code&#xff0c;掌握其核心…

【Java】JVM虚拟机(java内存模型、GC垃圾回收)

一、Java内存模型&#xff08;JMM&#xff09;JMM&#xff08;Java Memory Model&#xff0c;Java 内存模型&#xff09;是 Java 虚拟机规范中定义的一种抽象概念&#xff0c;用于规范 Java 程序中多线程对共享内存的访问规则&#xff0c;解决可见性、原子性和有序性问题&#…

二叉树算法之【二叉树的层序遍历】

目录 LeetCode-102题 LeetCode-102题 给定二叉树的根节点root&#xff0c;返回其节点值的层序遍历&#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 class Solution {public List<List<Integer>> levelOrder(TreeNode root) {// checkif (r…

uniapp+vue3——通知栏标题纵向滚动切换

介绍 取巧&#xff0c;使用纵向轮播实现 <!-- 通知栏 --> <view class"noticeBox" v-if"notice.length>0"><image src"/static/images/index/noticeIcon.png" mode"aspectFill"></image><swiper class&…

BilldDesk 开源、免费、吊打收费软件!白嫖党最爱!远程控制神器,没有任何连接次数和画质限制,同时显示多屏、屏幕墙等高级功能

远程控制软件哪个好用&#xff1f;TeamViewer收费太贵&#xff0c;向日葵限制太多&#xff0c;QQ远程又不稳定……别担心&#xff01;今天给大家推荐一款完全免费、开源的远程控制神器——BilldDesk&#xff01;它不仅功能强大&#xff0c;而且支持Windows、macOS、Linux、Andr…

ios UIAppearance 协议

一、前言 iOS 上提供了一个比较强大的工具UIAppearance&#xff0c;我们通过UIAppearance设置一些UI的全局效果&#xff0c;这样就可以很方便的实现UI的自定义效果又能最简单的实现统一界面风格。 (id)appearance ; 这个是这个协议里最重要的方法了 . 这个方法是统一全部改&am…

进阶数据结构:用红黑树实现封装map和set

​ 嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的 passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let’s go! 我的博客:yuanManGa…

【数据结构初阶】--二叉树(五)

&#x1f525;个人主页&#xff1a;草莓熊Lotso &#x1f3ac;作者简介&#xff1a;C研发方向学习者 &#x1f4d6;个人专栏&#xff1a; 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言&#xff1a;生活是默默的坚持&#xff0c;毅力是永久的…

redis布隆过滤器解决缓存击穿问题

在电商系统中&#xff0c;商品详情页是一个典型的高频访问场景。当用户请求某个商品的详情时&#xff0c;系统会优先从缓存中获取数据。如果缓存中没有该商品的详情&#xff0c;系统会去数据库查询并更新缓存。然而&#xff0c;如果某个热门商品的缓存失效&#xff0c;大量请求…