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

目录

动态规划的概念:

动态规划的解法流程:

题目: 第 N 个泰波那契数

解法(动态规划)

代码:

优化:

题目:最小花费爬楼梯

解法(动态规划)

解法1:

解法2:

题目:解码方法

解法(动态规划)

结语:


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

动态规划的概念:

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

动态规划的解法流程:

1.状态表示

dp问题的基础,自己要确定dp表每一个下标值的含义,这是用动态规划解决问题的第一步,只有把这一步确定了再去推出下面的状态转移方程,第一第二步完成后那么dp问题就已经解决了99%因为剩下的345就是处理边界和一些细节问题。

2.状态转移方程

推出状态转移方程可以说是dp问题最难的一步,如果在选定的状态表示下推不出状态转移方程,那么可能要换一个状态表示,因为状态表示可能是错误的。

3.初始化

一般初始化dp[0]和dp[1] .

4.填表顺序

一般有从左向右和从右先左,这取决于题目(覆盖问题)。

5.返回值

最后的返回值(不一定是dp[n]).

由于是算法只讲知识点是远远不够的,故下面我会用例题来帮助大家理解(例题的链接会在最后给出)。

到这一些基本概念就讲解完毕下面开始用题目要带友友更加深入学习。

题目: 第 N 个泰波那契数

题目链接1137. 第 N 个泰波那契数

泰波那契序列 Tn 定义如下: 

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2.

给你整数 ,请返回第 n 个泰波那契数 Tn 的值。

解法(动态规划)

1. 状态表示:

根据题目来推出状态表示,后面的大部分题目是要经验+题目来推出的

这道题可以「根据题目的要求」直接定义出状态表示:

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

2.状态转移方程

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

3.初始化

从我们的递推公式可以看出, dp[i] 在 i = 0 以及i = 1 的时候是没有办法进行推导的,因 为dp[-2] 或dp[-1] 不是⼀个有效的数据。因此我们需要在填表之前,将0, 1, 2 位置的值初始化。题目中已经告诉我们dp[0] = 0, dp[1] = dp[2] = 1 。(处理一些边界问题)

4.填表顺序

毫无疑问是「从左往右」。

5.返回值:

应该返回dp[n] 的值。

代码:

dp问题的代码编写流程一般比较固定分为1.创建dp表,2.初始化,3.填表,4.返回值.

最上面两个if用来解决边界问题。

class Solution {public int tribonacci(int n) {//1.创建dp表//2.初始化//3.填表//4.返回值int[] dp = new int[n + 1];if(n == 0){return 0;}if(n == 1 || n == 2){return 1;}dp[0] = 0;dp[2] = dp[1] = 1;for(int i = 3;i <= n;i++){dp[i] = dp[i - 3] + dp[i - 2] + dp[i - 1];}return dp[n];}
}

 

优化:

下面动图来自力扣。

一般是利用滚动数组优化(可以是一个小数组也可以是几个变量)

代码如下:

其实就是把表变成几个变量把空间复杂度降低到O(1)。

class Solution {public int tribonacci(int n) {//1.创建dp表//2.初始化//3.填表//4.返回值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;}
}

类似题:和上面那题类似给大家练手。

三步问题

参考代码如下:

其中dp[i] 表示:到达i位置时,⼀共有多少种方法。

通过分析知道第i步的方法为前三步方法的总和故状态转移方程为dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]。

class Solution {public int waysToStep(int n) {//1.创建dp表//2.初始化//3.填表//4.返回值//处理一下边界问题int MOD = (int)1e9 + 7;if(n == 1 || n == 2){return n;}if(n == 3){return 4;}int[] dp = new int[n + 1];dp[1] = 1;dp[2] = 2;dp[3] = 4;for(int i = 4;i <= n;i++){dp[i] = ((dp[i - 3] + dp[i - 2]) % MOD+ dp[i - 1]) % MOD;} return dp[n];}
}

题目:最小花费爬楼梯

题目链接:使用最小花费爬楼梯

注意这里的顶部不是数组的最后一个位置,而是在数组最后一个位置再后面一个。 

解法(动态规划)

解法1:

1. 状态表示:

这道题可以根据「经验+题⽬要求」直接定义出状态表示:dp[i] 表示:到达i 位置时的最小花费。(注意:到达i 位置的时候, i 位置的钱不需要算上)。

2.状态转移方程

根据最近的⼀步,分情况讨论:

(1)先到达i - 1 的位置,然后⽀付cost[i - 1] ,接下来⾛⼀步⾛到i 位置: dp[i - 1] + csot[i - 1] 。

(2)先到达i - 2 的位置,然后⽀付cost[i - 2] ,接下来⾛⼀步⾛到i 位置: dp[i - 2] + csot[i - 2] 。

3.初始化

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

4.填表顺序

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

5.返回值

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

代码:

class Solution {public int minCostClimbingStairs(int[] cost) {//1.创建dp表//2.初始化//3.填表//4.返回值int n = cost.length;int[] dp = new int[n + 1];dp[0] = 0;dp[1] = 0;for(int i = 2;i <=n;i++){dp[i] = Math.min((dp[i - 2] + cost[i - 2]),(dp[i - 1] + cost[i - 1]));}return dp[n];}
}

解法2:

解法一和解法二的区别就是状态表示不一样,这样再描述一个解法二是为了告诉大家解法不一定只有一种选定状态表示去试一下状态转移方程(不要怕错)。

1. 状态表示:

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

2.状态转移方程:

根据最近的⼀步,分情况讨论:

(1)支付cost[i] ,往后走⼀步,接下来从i + 1 的位置出发到终点: dp[i + 1] + cost[i] ;

(2)支付cost[i] ,往后走⼀步,接下来从i + 1 的位置出发到终点: dp[i + 1] + cost[i] ;

我们要的是最小花费,因此dp[i] = min(dp[i + 1], dp[i + 2]) + cost[i] 。

剩下三步我就不多赘述。

代码如下:

class Solution {public int minCostClimbingStairs(int[] cost) {//1.创建dp表//2.初始化//3.填表//4.返回值int n = cost.length;int[] dp = new int[n];dp[n - 1] = cost[n- 1];dp[n - 2] = cost[n - 2];for(int i = n - 3;i >= 0;i--){dp[i] = cost[i] + Math.min(dp[i + 1],dp[i + 2]);}return Math.min(dp[0],dp[1]);}
}

题目:解码方法

解法(动态规划)

1. 状态表示:

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

2.状态转移方程

关于i 位置的编码状况,我们可以分为下⾯两种情况:

(1)让i 位置上的数单独解码成⼀个字⺟。

(2)让i 位置上的数与i - 1 位置上的数结合,解码成⼀个字⺟。

让i位置上的数单独解码成⼀个字⺟,就存在「解码成功」和「解码失败」两种情况:

(1)当i位置上的数单独解码成⼀个字⺟。

解码成功:当i 位置上的数在[1, 9] 之间的时候,说明i 位置上的数是可以单独解 码的,那么此时[0, i] 区间上的解码⽅法应该等于[0, i - 1] 区间上的解码方法。因为[0, i - 1] 区间上的所有解码结果,后⾯填上⼀个i 位置解码后的字⺟就 可以了。此时dp[i] = dp[i - 1] ;

解码失败:当i 位置上的数是0 的时候,说明i 位置上的数是不能单独解码的,那么 此时[0, i] 区间上不存在解码⽅法。因为i 位置如果单独参与解码,但是解码失败了,那么前⾯做的努⼒就全部⽩费了。此时dp[i] = 0 。

(2)让i 位置上的数与i - 1 位置上的数结合,解码成⼀个字⺟。

解码成功:当结合的数在[10, 26] 之间的时候,说明[i - 1, i] 两个位置是可以 解码成功的,那么此时[0, i] 区间上的解码⽅法应该等于[0, i - 2 ]区间上的解码 ⽅法,原因同上。此时dp[i] = dp[i - 2] ;

解码失败:当结合的数在[0, 9] 和[27 , 99] 之间的时候,说明两个位置结合后解 码失败(这⾥⼀定要注意00 01 02 03 04 ......这⼏种情况),那么此时[0, i] 区间上的解码⽅法就不存在了,原因依旧同上。此时dp[i] = 0 。

综上所述: dp[i] 最终的结果应该是上⾯四种情况下,解码成功的两种的累加和(因为我们关⼼的是解码⽅法,既然解码失败,就不⽤加⼊到最终结果中去),因此可以得到状态转移⽅程( dp[i] 默认初始化为0 ):

(1)当s[i] 上的数在[1, 9] 区间上时: dp[i] += dp[i - 1] ;

(2)当s[i - 1] 与s[i] 上的数结合后,在[10, 26] 之间的时候: dp[i] += dp[i - 2] ;

如果上述两个判断都不成⽴,说明没有解码⽅法, dp[i] 就是默认值0 。

3.初始化

(1)当s[0] != '0' 时,能编码成功, dp[0] = 1 初始化dp[1] :

(2)当s[1] 在[1,9] 之间时,能单独编码,此时dp[1] += dp[0] (原因同上, dp[1] 默认为0 )

(3)当s[0] 与s[1] 结合后的数在[10, 26] 之间时,说明在前两个字符中,⼜有⼀种 编码⽅式,此时dp[1] += 1 。

4.填表顺序

毫⽆疑问是「从左往右」

5.返回值

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

代码:

class Solution 
{public int numDecodings(String ss) {// 1. 创建 dp 表 // 2. 初始化 // 3. 填表 // 4. 返回值 int n = ss.length();char[] s = ss.toCharArray();int[] dp = new int[n];if(s[0] != '0') dp[0] = 1; // 初始化第⼀个位置 if(n == 1) return dp[0]; // 处理边界情况 // 初始化第⼆个位置 if(s[1] != '0' && s[0] != '0') dp[1] += 1;int t = (s[0] - '0') * 10 + s[1] - '0';if(t >= 10 && t <= 26) dp[1] += 1;for(int i = 2; i < n; i++){// 先处理第⼀种情况 if(s[i] != '0') dp[i] += dp[i - 1];// 处理第⼆种情况 int tt = (s[i - 1] - '0') * 10 + s[i] - '0';if(tt >= 10 && tt <= 26) dp[i] += dp[i - 2];}return dp[n - 1];}
}

优化:

添加辅助位置初始化

可以在最前⾯加上⼀个辅助结点,帮助我们初始化。使⽤这种技巧要注意两个点:

(1)辅助结点⾥⾯的值要保证后续填表是正确的; 

(2)下标的映射关系

使用这种方式可以减少初始化的负担dp[1]就可以不用初始化,且不用考虑边界问题,因为我们的dp数组会开辟n+1,里面原本的数据都向后移动一位,dp[0]一般是0或者1(具体看题)。

代码如下:

class Solution {public int numDecodings(String s) {//1创建dp表//2初始化//3填表//4返回值char[] ss = s.toCharArray();int n = s.length();int[] dp = new int[n + 1];dp[0] = 1;if(ss[0] != '0'){dp[1] = 1;}for(int i = 2;i <= n;i++){if(ss[i - 1] != '0'){dp[i] += dp[i - 1];}int tt = (ss[i - 2] - '0') * 10 + ss[i - 1] - '0';if(tt >= 10 && tt <= 26){dp[i] += dp[i - 2];}}return dp[n];}
}

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

 

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

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

相关文章

【深度学习】Pytorch 教程(十一):PyTorch数据结构:4、张量操作(2):索引和切片操作

文章目录 一、前言二、实验环境三、PyTorch数据结构1、Tensor&#xff08;张量&#xff09;1. 维度&#xff08;Dimensions&#xff09;2. 数据类型&#xff08;Data Types&#xff09;3. GPU加速&#xff08;GPU Acceleration&#xff09; 2、张量的数学运算1. 向量运算2. 矩阵…

现在学Oracle是49年入国军么?

今天周末&#xff0c;不聊技术&#xff0c;聊聊大家说的最多的一个话题 先说明一下&#xff0c;防止挨喷&#x1f606; 本人并不是职业dba&#xff0c;对数据库就是爱好&#xff0c;偶尔兼职&#xff0c;以下仅个人观点分析&#xff0c;如有不同观点请轻喷&#xff0c;哈哈&…

U盘乱码与文件丢失:恢复指南与预防策略

U盘乱码文件丢失是一种常见的技术问题&#xff0c;通常表现为存储在U盘中的文件名显示为不可识别的字符或文件无法正常打开&#xff0c;有时甚至文件会完全消失。这种情况可能由多种原因引起&#xff0c;包括但不限于文件系统损坏、不正确的拔插操作、病毒感染、兼容性问题等。…

2024全国水科技大会暨土壤和地下水污染防治与修复技术创新论坛(七)

论坛召集人&#xff1a;李 辉 上海大学环境与化学工程学院教授 一、会议背景 十四五”时期&#xff0c;我国生态文明建设进入以减污降碳协同增效为重点战略方向&#xff0c;促进经济社会发展全面绿色转型&#xff0c;实现生态环境质量改善由量变到质变的关键时期。聚焦土壤与地…

【论文阅读】ICASSP 2023 针对目标检测的无目标后门攻击

文章目录 一.论文信息二.论文内容1.摘要2.引言3.作者贡献4.主要图表5.结论 一.论文信息 论文题目&#xff1a; Untargeted backdoor attack against object detection&#xff08;针对目标检测的无目标后门攻击&#xff09; 论文来源&#xff1a; 2023-ICASSP&#xff08;CCF…

访问raw.githubusercontent.com失败问题的处理

1 问题 GitHub上的项目的有些资源是放在raw.githubusercontent.com上的&#xff0c;通常我们在安装某些软件的时候会从该地址下载资源&#xff0c;直接访问的话经常容易失败。 # 安装operator kubectl apply -f https://raw.githubusercontent.com/oceanbase/ob-operator/2.1…

U版YOLO-World来了,YOLOv8再度升级,三行代码上手YOLO-World!

本文首发&#xff1a;AIWalker 欢迎关注AIWalker&#xff0c;近距离接触底层视觉与基础AI https://arxiv.org/abs/2401.17270 https://github.com/AILab-CVC/YOLO-World https://github.com/ultralytics/ultralytics https://www.yoloworld.cc/ YOLO-World亮点 YOLO-World是下…

c语言字符函数和字符串函数

目录 1. 字符分类函数2. 字符转换函数3. strlen的使用和模拟实现4. strcpy的使用和模拟实现5. strcat的使用和模拟实现6. strcmp的使用和模拟实现7. strncpy函数的使用8. strncat函数的使用9. strncmp函数的使用10. strstr的使用和模拟实现11. strtok函数的使用12. strerror函数…

数字化转型导师坚鹏:数据安全法解读与政府数字化转型

网络安全法、数据安全法、个人信息保护法解读与政府数字化转型 课程背景&#xff1a; 很多机构存在以下问题&#xff1a; 不清楚网络安全法、数据安全法、个人信息保护法立法背景&#xff1f; 不知道如何理解网络安全法、数据安全法、个人信息保护法政策&#xff1f; 不…

解决easyExcel模板填充时转义字符\{xxx\}失效

正常我们在使用easyExcel进行模板填充时&#xff0c;定义的变量会填充好对应的实际数据&#xff0c;未定义的变量会被清空&#xff0c;但是如果这个未定义的变量其实是模板的一部分&#xff0c;那么清空了就出错了。 在这张图里&#xff0c;上面的是模板填充后导出的文件&…

1906_ AMBA_高级MCU总线架构

1906_ AMBA_高级MCU总线架构 全部学习汇总&#xff1a; g_arm_cores: ARM内核的学习笔记 (gitee.com) 在看内核相关的文件的时候看到了AMBA这个缩写&#xff0c;查了一下具体的概念。这个其实是一个总线架构&#xff0c;应该是ARM设计的。我找到了相关的介绍网页&#xff1a; A…

[嵌入式系统-33]:RT-Thread -18- 新手指南:三种不同的版本、三阶段学习路径

目录 前言&#xff1a;学习路径&#xff1a;入门学习-》进阶段学习》应用开发 一、RT-Thread版本 1.1 标准版 1.2 Nano 1.3 Smart版本 1.4 初学者制定学习路线 1.5 RT-Thread在线文档中心目录结构 1.6 学习和使用RT-Thread的三种场景 二、入门学习阶段&#xff1a;内…

【爬虫逆向实战篇】定位加密参数、断点调试与JS代码分析

文章目录 1. 写在前面2. 确认加密参数3. 加密参数定位4. XHR断点调试 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向…

C++基础学习——哈希表的封装

目录 ​编辑 一&#xff0c;实现一个可封装的哈希表 1&#xff0c;哈希表的节点 2&#xff0c;哈希表的成员 3&#xff0c;哈希表成员方法的实现 4&#xff0c;迭代器的实现 5&#xff0c;在哈希表中加入迭代器 二&#xff0c;封装哈希表 1&#xff0c;unorder_map封装 2…

win10安装使用AxurePR9

背景&#xff1a;win10 安装、汉化 Axure Pr9 下载 安装包 链接&#xff1a;https://pan.baidu.com/s/1taMgh2zLbaFK7VTfUXTHdQ 提取码&#xff1a;kygo 安装 修改安装目录 打开是英文的 汉化 复制lang包到Axure安装包 再打开就是中文 问题 发布html后火狐无法打开 一、…

抖音数据挖掘软件|视频内容提取

针对用户获取抖音视频的需求&#xff0c;我们开发了一款功能强大的工具&#xff0c;旨在解决用户在获取抖音视频时需要逐个复制链接、下载的繁琐问题。我们希望用户能够通过简单的关键词搜索&#xff0c;实现自动批量抓取视频&#xff0c;并根据需要进行选择性批量下载。因此&a…

实现外网手机或者电脑随时随地远程访问家里的电脑主机(linux为例)

文章目录 一、背景概要二、安装配置花生壳软件(linux版本)三、手机端(外网)验证连接四、安装ubuntu20server版系统遇到的问题记录 一、背景概要 由于经常在遇到某些问题的时候&#xff0c;针对某一个场景的理解&#xff0c;需要借助于自己的电脑去编译(aosp/linux/qemu)代码查…

PHP语言检测用户输入密码及调用Python脚本

现在有一份计算流体力学N-S方程的Python脚本&#xff0c;想要在用户登录网站后可以可以运行该脚本&#xff0c;然后将脚本运行后绘制的图片显示在用户网页上。 建一个名为N_S.py的python脚本文件&#xff0c;这个脚本在生成图像后会自行关闭&#xff0c;随后将图片保存在指定的…

SpringMVC 学习(三)之 @RequestMapping 注解

目录 1 RequestMapping 注解介绍 2 RequestMapping 注解的位置 3 RequestMapping 注解的 value 属性 4 RequestMapping 注解的 method 属性 5 RequestMapping 注解的 params 属性&#xff08;了解&#xff09; 6 RequestMapping 注解的 headers 属性&#xff08;了解&…

【Android】坐标系

Android 系统中有两种坐标系&#xff0c;分别为 Android 坐标系和 View 坐标系。了解这两种坐标系能够帮助我们实现 View 的各种操作&#xff0c;比如我们要实现 View 的滑动&#xff0c;你连这个 View 的位置都不知道&#xff0c;那如何去操作呢&#xff1f; 一、Android 坐标…