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

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

目录

一、动态规划解题模版

  1. 状态表示:我们一般创建一个一维数组dp,把dp表填满,其中的某一个值就是结果。而状态表示就是指这个dp表中元素的含义;
    1.1. 来源:题目要求,经验+题目要求 ,分析问题的过程中的重复子问题
  2. 状态转移方程:dp[ i ] = ?
  3. 初始化:保证根据状态转移方程填表时不越界
  4. 填表顺序:为了填写当前状态的时候,所需要的状态已经计算过了
  5. 返回值:题目要求 + 状态表示

二、第N个泰波那契数

题目链接:第N个泰波那契数
题目描述:

  • 第四个数是钱三个数的和,第一二三个数定下位0 1 1,返回第n个数


题目解析:

解题思路:

  1. 状态表示:dp[ i ] 表示的 i 个泰波那锲数
  2. 状态转移方程:dp[ i ] = dp[ i - 1] + dp[ i - 2] + dp[ i - 3]
  3. 初始化:dp[ 0 ] = 0, dp[ 1 ]= 1, dp[ 2 ] = 1
  4. 填表顺序:顺序从左向右填表即可
  5. 返回值: dp[ n ]

解题代码:

//时间复杂度:O(N)//空间复杂度:O(N)classSolution{publicinttribonacci(int n){if(n <3)return n ==0?0:1;//创建dp表int[] dp =newint[n +1];//初始化 dp[0]=0; dp[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];}}

空间优化:只创建长度为三的数组即可:

//时间复杂度:O(N)//空间复杂度:O(1)classSolution{publicinttribonacci(int n){if(n <3)return n ==0?0:1;//创建dp表int[] dp =newint[]{0,1,1};//填表for(int i =3; i <= n; i++){int tmp = dp[0]; dp[0]= dp[1]; dp[1]= dp[2]; dp[2]= tmp + dp[0]+ dp[1];}//返回值return dp[2];}}

三、⾯试题 08.01. 三步问题

题目链接:⾯试题 08.01. 三步问题](https://leetcode.cn/problems/three-steps-problem-lcci/description/)

题目描述:

题目解析:

  • 每一次上楼梯有三个选择: 1 2 3 ,返回走 到第n阶梯 步有多少种走法

解题思路:

  1. 状态表示:dp[ i ] 表示第 i 阶的上楼方式种类数
  2. 状态转移方程:dp[ i ] = dp[ i - 1] + dp[ i - 2] + dp[ i - 3]
  3. 初始化:dp[ 0 ] = 1, dp[ 1 ]= 1, dp[ 2 ] = 2 (小孩有3种走法,那么小孩的上一个台阶的可能就是 i-1, i-2, i-3 )
  4. 填表顺序:顺序从左向右填表即可
  5. 返回值: dp[ n ]

解题代码:

//时间复杂度:O(N)//空间复杂度:O(N)classSolution{publicintwaysToStep(int n){if(n <3)return n;//建立dp表int[] dp =newint[n+1];//初始化 dp[0]= 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. 使⽤最⼩花费爬楼梯(easy)

题目链接:746. 使⽤最⼩花费爬楼梯(easy)

题目描述:

题目解析:

  • 给我们一个cost数组,表示走出这一个台阶需要的花费,
  • 可以选择第一步从cost[ 0 ] 还是 cost [ 1 ]开始。
  • 让我们返回到达楼顶的最小总花费。

解题思路:

  1. 状态表示:dp[ i ] 表示走到第 i 阶的最小花费
  2. 状态转移方程:dp[ i ] = dp[i-1]+cost[i-1] 和 dp[i-2]+cost[i-2] 的较小值
  3. 初始化:表的长度要比cost大1,表示楼顶
  4. 填表顺序:顺序从左向右填表即可
  5. 返回值: dp[ n ]
    解题代码:
//时间复杂度:O(N)//空间复杂度:O(N)classSolution{publicintminCostClimbingStairs(int[] cost){int n = cost.length;if(n ==2)returnMath.min(cost[0],cost[1]);//状态转移表int[] dp =newint[n+1];//状态转移方程for(int i =2; i <= n; i++){ dp[i]=Math.min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);}return dp[n];}}

五、91.解码⽅法

题目链接:91.解码⽅法

题目描述:

题目解析:

  • 给我们一个编码规则,数字1 - 26 对应表示字母A - Z
  • 给我们一个纯数字的字符串,让我们将字符串进行拆分,拆分后的字符串,对应的所有子串都可以对应表示成字母
  • 求可以拆分的种数

解题思路:

  1. 状态表示:dp[ i ] 表示字符串中 0 到 i 字符的合法拆分结果
  2. 状态转移方程:
    2.1. i 位置上字符为 1 到 9 的字符 单独解码成功 dp[ i ] += dp[ i - 1]
    2.2. i 与 i-1位置上的字符组合是 10 到 26 的合法编码 dp[ i ] += dp[ i-2 ]
  3. 初始化:
    3.1. dp[ 0 ] :第一个字符不为0,就dp[ 0] = 1,为0就返回0
    3.2. dp[ 1 ]:该字符不为0,dp[ 1 ] += dp[ 0 ],与第0个字符组成合法编码10 - 26 ,dp[ 1 ] += 1;

填表顺序:从左到右
返回值:dp[ n-1 ]

解题代码:

classSolution{publicintnumDecodings(String s){//前导0if(s.charAt(0)=='0')return0;int n = s.length();char[] ch = s.toCharArray();//只有一个字符if(n ==1)return1;int[] dp =newint[n];//初始化 dp[0]=1;//初始化第二个元素if(ch[1]!='0'&& ch[0]!='0') dp[1]+=1;int t =(ch[1]-'0')+(ch[0]-'0')*10;if(t >=10&& t <=26) dp[1]+=1;//填表for(int i =2; i < n; i++){//该字符单独组if(ch[i]!='0') dp[i]+= dp[i-1];//与前一个字符组 t =(ch[i]-'0')+(ch[i-1]-'0')*10;if(t >=10&& t <=26) dp[i]+= dp[i-2];}return dp[n-1];}}

Read more

C++ 游戏开发:从零到英雄的进阶之旅

C++ 游戏开发:从零到英雄的进阶之旅

在当今数字化时代,游戏开发已然成为极具吸引力与挑战性的领域。C++ 作为游戏开发中极为常用的语言之一,凭借其高性能和强大功能,长久以来都是游戏开发者的心头好。若你对游戏开发满怀热忱,却不知如何起步,这篇博客就将为你揭开 C++ 游戏开发的神秘面纱,引领你踏上从新手到高手的进阶之路。 一、为什么选择 C++ 进行游戏开发? 在游戏开发的广袤天地里,编程语言的抉择至关重要。C++ 以其独有的优势,成为众多开发者的不二之选: (一)高性能 游戏开发过程中需要处理海量的实时计算任务,涵盖图形渲染、物理模拟以及用户输入响应等关键环节。C++ 具备直接访问硬件的能力,能够极为高效地利用系统资源,切实保障游戏运行的流畅性。以处理复杂的 3D 场景渲染为例,C++ 能够快速对大量的顶点数据、纹理信息进行处理和计算,精准地将虚拟的 3D 世界呈现在玩家眼前,其性能优势在这种场景下展现得淋漓尽致。 (二)强大的功能 C++ 全力支持面向对象编程(OOP),这使得开发者能够通过类和对象来有条不紊地组织代码。比如在开发一款角色扮演游戏时,我们可以创建 “角色” 类,

By Ne0inhk
C++的IO流和C++的类型转换----《Hello C++ Wrold!》(29)--(C/C++)

C++的IO流和C++的类型转换----《Hello C++ Wrold!》(29)--(C/C++)

文章目录 * 前言 * C++的类型转换 * 四种命名的强制类型转换操作符 * static_cast * reinterpret_cast * const_cast * dynamic_cast * RTTI(这个了解一下就行了) * C++的IO流 * C++文件的IO流 * stringstream 前言 在 C++ 编程体系中,类型转换与 IO 流是支撑程序数据处理与交互的两大核心环节。类型转换关乎数据在不同类型间的安全传递与运算适配,而 IO 流则负责程序与外部设备(如键盘、屏幕、文件)之间的数据输入与输出,二者共同构成了 C++ 程序实现功能、交互信息的基础框架。 C 语言中的类型转换方式虽简洁,却存在可视性差、难以追踪的问题,容易在复杂程序中引发潜在的逻辑错误。为解决这一痛点,C++ 引入了四种命名明确的强制类型转换操作符 ——static_cast、reinterpret_

By Ne0inhk
C++ 多线程同步之条件变量(condition_variable)实战

C++ 多线程同步之条件变量(condition_variable)实战

C++ 多线程同步之条件变量(condition_variable)实战 💡 学习目标:掌握 C++ 标准库中条件变量的使用方法,理解条件变量与互斥锁的协同工作机制,能够解决多线程间的等待-通知问题。 💡 学习重点:std::condition_variable 的核心接口、wait() 与 notify_one()/notify_all() 的配合使用、生产者-消费者模型的实现。 49.1 条件变量的引入场景 在多线程编程中,我们经常会遇到线程需要等待某个条件满足后再执行的场景。 比如生产者线程生产数据后,消费者线程才能消费;队列不为空时,消费者才能从中取数据。 如果仅用互斥锁实现,消费者线程只能不断轮询检查条件,这会造成 CPU 资源的浪费。 ⚠️ 注意事项:单纯的轮询会导致 CPU 空转,降低程序运行效率,条件变量就是为解决这类问题而生的。 举个简单的轮询反例,消费者不断检查队列是否有数据: #include<iostream>

By Ne0inhk
C++ 仿函数详解:让对象像函数一样调用

C++ 仿函数详解:让对象像函数一样调用

前言 在 C++ 中,仿函数(Functor) 是指重载了 operator() 的类或结构体的对象,它们的行为类似于普通函数,因此可以像函数一样被调用。仿函数在 STL 算法、回调机制、函数适配器等场景中有着广泛的应用。本文将深入探讨仿函数的概念、优点、使用方式,并结合具体示例进行详细解析。 1. 为什么需要仿函数? 在 C++ 中,我们可以用普通函数或 std::function(C++11 引入)来定义可调用对象,但仿函数相比之下有以下优势: * 状态存储:普通函数无法存储状态,而仿函数可以在对象内部维护状态,例如计数器、阈值等。 * 性能优化:由于仿函数是类的实例,可以通过内联优化减少函数调用的开销。 * 与 STL 兼容:STL 容器和算法广泛使用仿函数,如 std::sort() 可接受仿函数作为自定义排序规则。

By Ne0inhk