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

您所在的位置:网站首页 python123斐波那契数列 【动态规划】斐波那契数列模型

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

2023-06-14 23:02| 来源: 网络整理| 查看: 265

冻龟算法系列之斐波那契数列模型

在这里插入图片描述

文章目录 【动态规划】斐波那契数列模型1. 第N个泰波那契数1.1 题目解析1.2 算法原理1.2.1 状态表示1.2.2 状态转移方程1.2.3 初始化1.2.4 填表顺序1.2.5 返回值 1.3 编写代码1.4 空间优化 2. 三步问题2.1 题目解析2.2 算法原理2.2.1 状态表示2.2.2 状态转移方程2.2.3 初始化2.2.4 填表顺序2.2.5 返回值 2.3 编写代码 3. 使用最小花费爬楼梯3.1 题目解析3.2 算法原理3.2.1 状态表示3.2.2 状态转移方程3.2.3 初始化3.2.4 填表顺序3.2.5 返回值 3.3 编写代码3.4 解法2:以某节点为起点3.4.1 得到状态转移方程3.4.2 初始化3.4.3 编写代码 4. 解码方法4.1 题目解析4.2 算法原理4.2.1 状态表示4.2.2 状态转移方程4.2.3 初始化4.2.4 填写顺序4.2.5 返回值 4.3 编写代码4.4 代码简化技巧

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

动态规划(英语:Dynamic programming,简称 DP),是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。

至于这个模型是什么意思,我觉得你往下看下去,自主感受那些相似之处,才不会懵逼~

而本文为动态规划系列的第一篇,所以在动态规划做题步骤上会比较分明,接下来的几道例题,让我们边实战边学吧! 文章解题的大的流程为:

题目解析算法原理(可能会有其他解法,在这个系列只讲解动态规划的思想)编写代码空间优化 动态规划一般很难,做出来已经不错了,所以主要重心放在解题上本文第一道题,把所有流程都会过一遍,所以会讲,而在讲背包问题之前,都不会出现这个步骤因为动态规划一般会比较浪费空间,所以才有这个空间优化的必要 1. 第N个泰波那契数

传送门:力扣1137

题目:

在这里插入图片描述

1.1 题目解析

在这里插入图片描述

所以有以下示例:

  [

同理:

在这里插入图片描述

1.2 算法原理

算法原理分析的过程分五步:

画出dp表并确定dp表**状态表示**根据状态表示确定**状态转移方程****初始化**dp表确定**填表顺序**确定**返回值**

由于是第一道题,所以下面是细致讲解!

但是因为这道题比较简单,一些方面没有涉及到,但是对于初学者,这样才最友好,因为本题重点就是熟悉做题流程!在后续的学习中,反复积累经验,了解方方面面,再依此基础上去刷题,才能学好动态规划! 1.2.1 状态表示

什么是状态表示:

这就涉及动态规划的核心:dp表

这个dp表的建立是不固定的,可能是一维数组,二维数组等等…

而dp表中的元素与其对应下标有什么关系,即这个元素的**含义**,或者说是这个元素所处的“状态”(抽象笼统的说法)

而其中,我们要的答案,就是dp表其中的一个元素

例子:

开心or不开心,是状态1下标元素的含义就是滑稽老铁开心

在这里插入图片描述

当然,dp表代表的状态一般不是对立的,而是这样的

在这里插入图片描述

而之后我们可以 以“含义”去理解

而设立的状态表示不一样,也意味着思路不一样,即不同的解法~

想到一个,然后就去试试能不能解…做多点题,这一步准确率会高点~

怎么来?

经验 + 题目要求分析问题的过程中,发现重复的子问题,抽象成状态表示(暂时不讲)

而这道题,我们只需建立一个一维dp表(大小为n + 1):

题目要求:返回第n个泰波那契数的值经验:dp[n]应该就是答案

在这里插入图片描述

即dp[i]表示:第 i 个泰波那契数的值

1.2.2 状态转移方程

什么是**状态转移方程**:

就是,dp[i] = … ;而这个方程可能有条件,分支,循环等复杂的逻辑…

而dp[i]是由dp表其他下标对应的值来推导出来的

比如,dp[i]之前或者之后的值可以推出dp[i]

例子:

开心是会传染的:

在这里插入图片描述

而本题的动态转移方程就是:

dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];

难的方程怎么推导? 遇到再说现在纸上谈兵没有意义

对应动态规划的题目而言,前两步是最最重要的,也就是百分之99已完成~

接下来就是一些细节的处理

1.2.3 初始化 很明显,通过已知推未知的过程中,会遇到一些数组越界等问题…,而初始化就是为了解决这些问题

本题为:

i - 1,i - 2,i - 3要有意义,则i要大于等于3所以在初始化的时候,手动给dp[0]、dp[1]、dp[2]赋值(赋值之前要确保不越界,越界就直接返回即可) 1.2.4 填表顺序

我们通过已知推未知,这里的已知可能是i之前或者i之后,这就分为了简单的两个大方向(一维dp表) 在这里插入图片描述

本题为:顺序1

要保证填这个下标的时候,需要用到的其他下标,是已经在此之前填了的! 即,计算过了的 1.2.5 返回值

根据题目要求 + 状态表示得出返回值

本题为:dp[n]

注意下标确定是哪个可以说返回值和状态表示是同时确定的 1.3 编写代码

编写代码分为固定的四步:

创建dp表初始化算法主体(填表)返回值 class Solution { public int tribonacci(int n) { //1. 创建dp表 int[] dp = new int[n + 1]; //2. 初始化 if(n == 0) { return 0; } if(n == 1 || n == 2) { return 1; } dp[0] = 0; dp[1] = 1; dp[2] = 1; //3. 填表 for(int i = 3; i public int tribonacci(int n) { //1. 空间优化 int[] src = new int[3]; src[0] = 0; src[1] = 1; src[2] = 1; //2. 初始化 if(n == 0) { return 0; } if(n == 1 || n == 2) { return 1; } int ret = 0; //3. 填表 for(int i = 3; i public int waysToStep(int n) { //1. 创建表 int[] dp = new int[n + 1]; int MOD = (int)1e9 + 7; //取模数 //2. 初始化,处理边界问题 if(n == 1) { return 1; } if(n == 2) { return 2; } if(n == 3) { return 4; } dp[1] = 1; dp[2] = 2; dp[3] = 4; //3. 填表 for(int i = 4; i public int minCostClimbingStairs(int[] cost) { int n = cost.length; //1. 创建dp表 int[] dp = new int[n + 1]; //2. 初始化,处理边界 if(n == 0 || n == 1) { return 0; } dp[0] = 0; dp[1] = 0; //3. 填表 for(int i = 2; i public int minCostClimbingStairs(int[] cost) { int n = cost.length; //1. 创建dp表 int[] dp = new int[n + 1]; //2. 初始化,处理边界 if(n == 0 || n == 1) { return 0; } dp[n] = 0; dp[n - 1] = cost[n - 1]; //3. 填表 for(int i = n - 2; i >= 0; i--) { dp[i] = Math.min(dp[i + 1], dp[i + 2]) + cost[i]; } //4. 返回值 return Math.min(dp[0], dp[1]); } }

在这里插入图片描述

所以状态表示的不同,算法思路就有所不同,至于怎么选中呢?

做题多,经验多想到一个,就想尽方法去用这一个去解决问题,解决不了再换(敢于尝试) 4. 解码方法

传送门:力扣91

题目:

在这里插入图片描述

4.1 题目解析

在这里插入图片描述

对于示例,目前看不出什么~ 在这里插入图片描述

4.2 算法原理 4.2.1 状态表示

显然,也是一维的dp表(大小为n)

题目要求:输入字符串,返回整个字符串能反解出来的解的个数

经验:以某个节点为终点

那么这个终点只能是字符串的某个字符了~而答案应该为dp[n - 1](dp表的其中一值)

在这里插入图片描述

那么我们趋向于让答案合理的为dp[n - 1]:

得到状态表示:dp[i]代表字符串[0, i]的子串最多能反解出来的解的个数

4.2.2 状态转移方程

同样的,我们把dp[i]之前的值视为“已知”,把它们当做条件,去推导dp[i]

那么,要扩展到[0, i]的字符串,则扩展成[0, i]字符串之前,应该为[0, i - 1]的子串或者[0, i - 2]的子串:

如果是[0, i - 1]的子串,则字符串的第i个字符应该单独转化为字母才行,不然前功尽弃! 反解成功:dp[i - 1]反解失败:0 如果是[0, i - 2]的子串,则字符串的第i - 1和第i个字符应该成双转化为字母(06之类的不行!)才行,否则前功尽弃! 反解成功:dp[i - 2]反解失败:0为什么不可以各自可转化为字母也行? 第i - 1个字符可以单独反解和第i个字符也可以单独反解,此解法在情况1出现过

得出状态转移方程:

dp[i] = (dp[i - 1]) + (dp[i - 2]);

前提是对应项满足条件 4.2.3 初始化

对于0和1下标要特殊处理:

0的话

如果可以单独成字母 => 1不可以 => 0

1的话

成双结合成字母 => +1各自单独成字母 => +1都不可以 => 0 4.2.4 填写顺序

由于是以某个节点为终点,所以填写顺序为从左到右~

4.2.5 返回值

返回dp[n - 1],代表整个字符串能反解的解的个数

4.3 编写代码 class Solution { public int numDecodings(String s) { int n = s.length(); char[] string = s.toCharArray(); //创建dp表 int[] dp = new int[n]; //初始化并处理边界问题 if(string[0] != '0') { dp[0] = 1; } if(n == 1) { return dp[0]; } if(string[0] !='0' && string[1] != '0') { dp[1]++; } int number = (string[0] - '0') * 10 + string[1] - '0'; if(number >= 10 && number if(string[i] != '0') { dp[i] += dp[i - 1]; } number = (string[i - 1] - '0') * 10 + string[i] - '0'; if(number >= 10 && number public int numDecodings(String s) { int n = s.length(); char[] string = s.toCharArray(); int[] dp = new int[n + 1]; dp[0] = 1; if(string[0] != '0') { dp[1] += 1; } if(n == 1) { return dp[1]; } for(int i = 2; i dp[i] += dp[i - 1]; } int number = (string[i - 2] - '0') * 10 + string[i - 1] - '0'; if(number >= 10 && number


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3