动归专题——斐波纳契模型和路径问题

前提

本专题开始,注重整理与动态规划相关的题目,从简单的动归开始切入,慢慢掌握和练习动态规划这一重要的算法思想,部分相对复杂的题目会结合画图和代码分析去解释

一、第N个泰波纳契数列

1.链接

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

2.描述

3.思路

关于动态规划的题目,往后都是按照五步走的统一思路去进行分析

(1)状态表示

状态表示我们通常是根据经验加对具体题目的判断去设置的,通常的设置思路有:

以第i个位置为底,结合题目条件...

以第i个位置开始,结合题目条件...

...

本题状态表示:f(i)表示以i位置为底,泰波那契数列的值

(2)状态转移方程

状态方程的判断一般需要我们根据具体的题目条件去进行推导,这题目的方程式就是状态转移方程

f(i) = f(i-1)+ f(i-2) + f(i-3)

(3)初始化

初始化是为了确定边界条件,确定起始位置

f(0)= 0,f(1)= 1,f(2) = 1;

(4)填表顺序

写代码时,我们需要一个dp表,我们需要根据状态方程去判断填表顺序是从如何推导,本题的推导顺序很明显是从头往后推导记录

(5)返回值

确定返回的值,这个返回值根据题目不同,可能返回的不同,本题是返回dp[ n ]

4.参考代码

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表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];}
};

二、三步走问题

1.链接

面试题 08.01. 三步问题 - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

以第n个台阶为底(一共有n个台阶),小孩一共有多少种走法

(2)状态转移方程

dp[ n ] = dp[n - 1] + dp[n - 2] + dp[n - 3]

(3)初始化

dp[1] = 1; dp[2] = 2; dp[3] = 4

(4)填表顺序

从左到右

(5)返回值

dp[ n ]

4.参考代码

class Solution {
public:const int num = 1e9+7;int waysToStep(int n) {if(n == 1 || n == 2) return n;if(n == 3) return 4;vector<int> dp(n+1);dp[1] = 1; dp[2] = 2; dp[3] = 4;for(int i = 4;i <= n;i++){dp[i] = ((dp[i-1] + dp[i-2])%num + dp[i-3])%num;}return dp[n];}
};

三、使用最小花费爬楼梯

1.链接

746. 使用最小花费爬楼梯 - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

思路一:以i为结束,...

f(i)表示爬到第i层,花费最少的费用

思路二:以i为开始,...

f(i)表示从第i层开始,到楼顶需要花费最少的费用

(2)状态表示方程

思路一: f(i)= min ( f(i -1)+cost[i-1] ,f(i-2)+cost[i-2] )

思路二: f(i)= min( f(i+1) , f(i+2) ) + cost [ i ]

(3)初始化

思路一:f(0)= 0  ;   f( 1 ) = 0

思路二:f(n)= cost[ n ] ;   f(n-1)= cost[n-1]

(4)填表顺序

思路一:从左到右

思路二:从右到左

(5)返回值

思路一:dp[n] 

思路二:dp[0]

4.参考代码

思路一:

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {vector<int> dp(cost.size()+1);dp[0] = 0;dp[1] = 0;for(int i = 2;i<=cost.size();i++){dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);}return dp[cost.size()];}
};

思路二:

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n = cost.size()-1;vector<int> dp(n+1);dp[n] = cost[n];dp[n-1] = cost[n-1];for(int i = n-2;i>=0;i--){dp[i] = min(dp[i+1],dp[i+2])+cost[i];}return min(dp[0],dp[1]);}
};

四、解码方法

1.链接

. - 力扣(LeetCode)

2.描述

3.思路

4.参考代码

class Solution 
{
public:int numDecodings(string s) {int n = s.size();//由于dp[0]要作为辅助位置,因此映射关系整体向后移//也可以认为此刻dp[i]对应的就是从1开始往后数的第i个字母vector<int> dp(n+1,0);dp[0] = 1;dp[1] = s[0] != '0';for(int i = 2;i<=n;i++){if(s[i-1]!='0')//i位置的数字单独解码成功dp[i]+=dp[i-1];int second = (s[i-2]-'0')*10+(s[i-1]-'0');if(second>=10 && second<=26)//第二种情况解码成功dp[i]+=dp[i-2];}return dp[n];}
};

五、不同路径

1.链接

62. 不同路径 - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

从左上角出发,走到[ i , j ]位置时一共有dp(i,j)种办法

(以[i,j]为结束,...)

(2)状态表示方程

(3)初始化

初始化需要考虑边界问题,通常我们会选择给路径多加上一层边框,然后对边框进行合理的赋值

(4)填表顺序

从左到右,从上到下,就是遍历二维数组的顺序

(5)返回值

dp[m,n]

4.参考代码

class Solution {
public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m+1,vector(n+1,0));dp[0][1] = 1;for(int i =1;i<=m;++i){for(int j = 1; j<=n;++j){dp[i][j] = dp[i-1][j]+dp[i][j-1];}}return dp[m][n];}
};

六、不同路径||

1.链接

63. 不同路径 II - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

dp[i,j] :  以[i,j]位置结束,一共有dp[i,j]种路线

(2)状态表示方程

(3)初始化

和上题的初始化一样,不过这里就需要注意映射关系了,在题目给的障碍表格中要注意映射关系

(4)填表顺序

从左上角到右下角

(5)返回值

dp[m,n]

4.参考代码

class Solution {
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m = obstacleGrid.size();int n = obstacleGrid[0].size();vector<vector<int>> dp(m+1,vector(n+1,0));dp[0][1] = 1;for(int i = 1;i<=m;i++){for(int j = 1;j<=n;j++){if(obstacleGrid[i-1][j-1] != 1){dp[i][j] = dp[i-1][j] + dp[i][j-1];}}}return dp[m][n];}
};

七、珠宝的最高价值

1.链接

LCR 166. 珠宝的最高价值 - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

dp[i,j] 表示 以[i,j]位置结束,所获得的最大珠宝价值

(2)状态表示方程

有了上面两题的经验,不难分析出,[i,j]位置获得的最大珠宝价值,会等于当前位置的珠宝价值加上max(dp[i-1,j],dp[i,j-1])

dp[i,j] = max(dp[i-1,j],dp[i,j-1])+frame[ i ][ j ] (实际代码中要注意具体映射位置)

(3)初始化

和前两题一样的初始化方式,但是本题中不需要对第一个位置进行赋值为1,因为此时dp记录的是珠宝的价值,因此起始是不需要赋值的,全部初始化为0即可

(4)填表顺序

从左上角到右下角

(5)返回值

return dp[m][n]

4.参考代码

class Solution {
public:int jewelleryValue(vector<vector<int>>& frame) {int m = frame.size();int n = frame[0].size();vector<vector<int>> dp(m+1,vector(n+1,0));for(int i = 1;i<=m;i++){for(int j = 1;j<=n;j++){dp[i][j] = max(dp[i-1][j],dp[i][j-1])+frame[i-1][j-1];}}return dp[m][n];}
};

八、下降路径最小和

1.链接

931. 下降路径最小和 - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

这次我们换一个思路,“以i位置开始,...”

以当前位置[ i ][ j ] 开始,走到最底下的最小路径和为dp[ i ][ j ]

(2)状态表示方程

(3)初始化

我们通过题目可以看到,每次dp遍历选择时,最左边那一列和最右边那一列存在越界的问题,因此我们可以给左右两边各加上一列去解决边界越界的问题,同时由于题目计算的是最小路径和,辅助列相当于非法路径,我们可以给其赋值为最大值,即可保证dp不会选择非法路径,再往最后一行添加一行辅助行,实现最后一列dp的赋值

(4)填表顺序

从最后一行往上填表,从下到上,从左往右

(5)返回值

取第一行中最小值返回

4.参考代码

这种思路到这来在映射关系上会比较复杂,需要画图,这题是为了练习另一种思路,推荐使用:

以[i][j]位置结束,所需的最小路径和为dp[i][j]的思路会更好写代码

class Solution {
public:int minFallingPathSum(vector<vector<int>>& matrix) {int m = matrix.size();int n = matrix[0].size();vector<vector<int>> dp(m+1,vector<int>(n+2,INT_MAX));//初始化for(int i = 0;i<=n+1;i++){dp[m][i] = 0;}for(int i = m-1;i>=0;i--){for(int j = 1;j<=n;j++){dp[i][j] = min(min(dp[i+1][j-1],dp[i+1][j]),dp[i+1][j+1]) + matrix[i][j-1];}}int ret = dp[0][1];for(int i = 2;i<=n;i++){ret = min(ret,dp[0][i]);}return ret;}
};

九、最小路径和

1.链接

64. 最小路径和 - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

以[i][j]位置为结束,最小的路径和为dp[i][j]

(2)状态表示方程

dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i][j]

(3)初始化

多加一行和一列的辅助行和辅助列,初始化为最大值,然后让dp[0][1] = 0,确保第一个左上角位置的路径值是正确的,最大值则是保证其他路径不会选择非法路径

(4)填表顺序

从左上角到右下角

(5)返回值

dp[m][n]

4.参考代码

class Solution {
public:int minPathSum(vector<vector<int>>& grid) {int m = grid.size();int n = grid[0].size();vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));dp[0][1] = 0;for(int i = 1;i<=m;++i){for(int j = 1;j<=n;j++){dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i-1][j-1];}}return dp[m][n];}
};

十、地下城游戏

1.链接

174. 地下城游戏 - 力扣(LeetCode)

2.描述

3.思路

(1)状态表示

根据题意,这里若是使用以[i][j]位置结束的思路,则不能保证后续路程可以走完,要采用另一种思路

dp[i][j] : 以[ i ][ j ]位置开始,走到最后所需要消耗的最小健康值

(2)状态表示方程

dp[i][j] = min( dp[i+1][j],dp[i][j+1] ) - dungeon[ i ][ j ]

我们可以认为,某个位置的点数如果是正数则就是回血,可以减少消耗,若是负数则加上增加了消耗,并且,还要考虑到消耗不能为负数,负数意味着有一个大血包,但勇士不能从0血以下开始走,当遇到大血包时,我们认为该点走到结尾的消耗为0

(3)初始化

我们需要辅助层去保证不越界的问题,dp起始位置是在末尾(右下角),为了不越界,我们需要给辅助层初始化为最大值,而为了让第一个dp填对,我们要让其相邻的其中一个为0(dp[i+1][j] =0)

(4)填表顺序

从右下角开始,从右往左,从下往上

(5)返回值

return dp[0][0]+1;

4.参考代码

class Solution {
public:int calculateMinimumHP(vector<vector<int>>& dungeon) {int m = dungeon.size();int n = dungeon[0].size();vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));dp[m][n-1] = 0;for(int i = m-1;i>=0;--i){for(int j = n-1;j>=0;--j){dp[i][j] = min(dp[i+1][j],dp[i][j+1]) - dungeon[i][j];dp[i][j] = max(0,dp[i][j]);}}return dp[0][0] + 1;}
};

总结

本篇总结了关于动态规划的一些题目,斐波那契数列类型的和路径问题相关的动态规划经典题目

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

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

相关文章

C# NumericUpDown 控件正整数输入控制

用到了控件的 KeyPress 和 KeyUp事件。 KeyPress 中控制输入“点、空格&#xff0c;负号”&#xff1b; KeyUp 中防止删空&#xff0c;以及防止输入超过最大值或最小值 。 private void nudStart_KeyPress(object sender, KeyPressEventArgs e){numericUpDownKeyPress(sender…

Python时间

UTC ~ 北京时间 【差8小时】 格式化日期时间为字符串:strftime 时间戳-1970.1.1到现在的秒数:time.time() AttributeError: partially initialized module ‘datetime’ has no attribute ‘fromtimestamp’ (most likely due to a circular import) 改正&#xff1a;文件名和…

合同起草、审查耗时费力?君子签智能电子合同让签署事半功倍

在合同签署过程中&#xff0c;着急和客户签约&#xff0c;却找不到合适的内容范本&#xff1f;法务审查合同只能逐字逐句审查&#xff0c;效率太慢&#xff1f;合同需要客户、法务、负责人等多方参与&#xff0c;修改内容难以一一对应&#xff1f;合同涉及的工资、费用、价格等…

WordPress网站已经安装了SSL证书,但浏览器仍然提示不安全

WordPress网站已经安装了SSL证书&#xff0c;但浏览器仍然提示不安全 昨天我们新建了一个WordPress的网站&#xff0c;在已经安装了SSL证书的情况下&#xff0c;访问网站仍然会提示不安全。 我们使用的是Hostease提供的虚拟主机产品&#xff0c;之前从未出过这样的情况&#x…

用友BI方案:一键搞定财务、销售、生产等分析

别人家做用友的数据分析&#xff0c;从零开始搭建分析模型、设计BI数据可视化报表&#xff0c;反复测试修改&#xff0c;耗时耗力&#xff0c;效果未知。奥威BI做用友的数据分析&#xff0c;用用友BI方案&#xff0c;注册、下载执行&#xff0c;自动取数、计算指标、分析数据、…

达梦数据库自动备份(全库)+还原(全库) 控制台

一 前提 1.安装达梦数据库DB8(请参照以前文章) 我的数据库安装目录是 /app/dmDB8 2.已创建实例 (请参照上一篇文章) 二 准备测试数据 三 自动备份步骤 1.开启归档模式 开启DM管理工具管理控制台 弹不出来工具的 输入命令 xhost 第一步 将服务器转换为配置状态 右键-&g…

后端常问面经之Spring和Mybatis框架

Spring的IOC介绍一下&#xff1a; 所谓控制就是对象的创建、初始化、销毁。 创建对象&#xff1a;原来是 new 一个&#xff0c;现在是由 Spring 容器创建。 初始化对象&#xff1a;原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值&#xff0c;现在是由 Spring 容器…

AI研报:从Sora看多模态大模型发展

《从Sora看多模态大模型发展》的研报来自浙商证券&#xff0c;写于2024年2月。 这篇报告主要探讨了多模态大模型的发展趋势&#xff0c;特别是OpenAI发布的视频生成模型Sora&#xff0c;以及其对行业发展的影响。以下是报告的核心内容概述&#xff1a; Sora模型的发布&#x…

面试算法-103-对链表进行插入排序

题目 给定单个链表的头 head &#xff0c;使用 插入排序 对链表进行排序&#xff0c;并返回 排序后链表的头 。 插入排序 算法的步骤: 插入排序是迭代的&#xff0c;每次只移动一个元素&#xff0c;直到所有元素可以形成一个有序的输出列表。 每次迭代中&#xff0c;插入排序…

基于单片机的太阳能充电系统设计

摘要:本文所设计的太阳能充电系统主要由以下几个模块组成:STC89C52 主控模块、TP4056 充电电路、电压AD 采集模块、LCD1602 液晶显示模块和太阳能充电电池等组成。此太阳能充电器制作简单,性价比高,性能稳定。 关键词:LCD1602;太阳能充电系统;ADC0832 太阳能充电系统的充…

MySQL WHERE 条件查询

我们通常要求在执行 SELECT 查询时&#xff0c;都要带上查询条件。那这一节&#xff0c;我们就来学习一些简单的 WHERE 条件查询。 我们仍然以技术派文章表 article 为例&#xff0c;比如说我们要查找标题为“聊聊分库分表”的文章&#xff0c;可以这么写&#xff1a; SELECT *…

echarts做水滴图;解决[echarts] unknown series liquidfill 水球加载问题

一份echarts示例代码&#xff0c;包含水滴图 直接在echarts里使用水滴图liquidfill会报错[echarts] unknown series liquidfill 解决方案&#xff1a;需要下载echarts-liquidfill依赖 echarts-liquidfill2兼容echarts4; echarts-liquidfill3兼容echarts5; 例如&#xff1a;我的…

IDEA使用手册

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【vue3(七)】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、ref二、TS接口泛型规范1.创建ts文件&#xff0c;定义接口2.引入规范 三、props的使用四、生命周期&#xff08;生命周期函数&#xff0c;生命周期钩子&…

rust中字符串String常用方法和注意事项

Rust 中通常说的字符串指的是&#xff1a;String 和 &str(字符串字面值、或者叫字符串切片)这两种类型。str是rust中基础字符串类型&#xff0c;String是标准库里面的类型。Rust 中的字符串本质上是&#xff1a;Byte的集合&#xff08;Vec<u8>&#xff09; 基础类型…

【第三方登录】Google邮箱

登录谷歌邮箱开发者 https://console.developers.google.com/ 先创建项目 我们用的web应用 设置回调 核心主要&#xff1a; 1.创建应用 2.创建客户端ID 3.设置域名和重定向URL 4.对外公开&#xff0c;这样所有的gmail邮箱 都能参与测试PHP代码实现 引入第三方包 h…

Spring实例化Bean的三种方式

参考资料&#xff1a; Core Technologies 核心技术 spring实例化bean的三种方式 构造器来实例化bean 静态工厂方法实例化bean 非静态工厂方法实例化bean_spring中有参构造器实例化-CSDN博客 1. 构造函数 1.1. 空参构造函数 下面这样表示调用空参构造函数&#xff0c;使用p…

linux查看usb是3.0还是2.0

1 作为device cat /sys/devices/platform/10320000.usb30drd/10320000.dwc3/udc/10320000.dwc3/current_speed 或 /sys/class/udc/10320000.dwc3/current_speed 如下 high-speed usb2.0 super-speed usb3.0 2 作为host linux下使用以下命令查看 &#xff0c;如果显示 速率为…

python关于字符串基础学习

字符串 python字符串是不可改变的 Python不支持单字符类型&#xff0c;单字符也是作为一个字符串使用的。 字符串编码 python3直接支持Unicode,可以表示世界上任何书面语言的字符 python3的字符默认就是16位Unicode编码&#xff0c;ASCII是Unicode的子集 使用内置函数 ord()…

c++初步

作业&#xff1a; 定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定义一个全局函数完成对该字符串的反转 #include <iostream> #include &…