算法·动态规划Dynamic Programming

很多人听到动态规划或者什么dp数组了,或者是做到一道关于动态规划的题目时,就会有一种他很难且不好解决的恐惧心理,但是如果我们从基础的题目开始深入挖掘动规思想,在后边遇到动态规划的难题时就迎难而解了。
其实不然,动态规划类题目确实很不好发现解题思路,如何成为高手呢?那就是多刷题,但是还是像上边所说,不要刚接触就上难题只会让自己望而却步,我们只有不断积累经验,才能完全拿捏动态规划里面的精髓。
动态规划算法思想的核心
有资料这样解释动态规划过程:
将问题分解为多个阶段,每个阶段对应一个决策,我们可以记录每个阶段所得到的状态的集合(去掉重复的),然后根据当前的状态的集合推导出下一阶段的状态集合,动态的向前推进,直至到达最终阶段,得到我们想要的结果。
有一位博主很巧妙地说明了上边所说的含义
A:问“1+1+1+1+1+1+1+1+1”等于几?
B:等于9A:在表达式前边加上1+后结果等于几?
B:等于10(迅速)
A:为什么这么快得到结果呢?
B:因为我已经知道1+1+1+1+1+1+1+1+1等于9,再加1就得到10了
,而不是重新再算一遍。
ok,了解了动态规划的思想,我们就可以来探寻遇到动态规划的问题时,我们应该怎么求解
首先,用一个简单的dp问题进行入门
第N个泰波那契数

在做每道题时,我们首先要来解析题目
我们已经知道第0,1,2个数的值了,我们再来看后边的表达式,很明显,有了前边的值还有了这个表达式,我们可以求出任意n位置的值。
我们可以用动态规划的分析方法来分析这道题目
-
状态表示
我们可以创建一个数组

这个数组就叫做dp表,我们所要做的就是把dp表中的数填满,然后就可以得到我们的最终结果。
那么状态表示是什么意思呢?
状态表示就是dp[i]位置上元素的含义,dp[i]在这道题目中就表示第i个泰波那契数的值。
这道题目的状态表示很容易就能看出来,但遇到难题后,会遇到其他状态表示的方式,有一些会比较抽象,但是我们可以从浅入深,慢慢来嘛。 -
状态转移方程
这道题目的状态转移方程题目已经给出来咯,如果后边遇到某些题没给呢?那当然是自己找规律,很简单的。
dp[i]=dp[i-1]+dp[i-2]+dp[i-3];
- 初始化
如果知道了状态转移方程,然而不知道如何求出dp数组中的元素,那不就是没用嘛,所以前边给出我们前三个状态的值,由此我们才可以知道第四个,第五个的值。
所以初始化还是很重要的,根据题目所示,将我们构建出的数组进行初始化。 - 填表顺序
这道题的填表顺序明显是从左到右,知道了前边数组的元素,我们才能根据状态转移方程来求解右边的值。
你是怎么知道填表顺序的呢?
确定填表顺序,为了填写当前状态表示的时候,所需要的状态已经计算过了。就像这道题,我们要求dp[4]的值,然而dp[3]我们都不知道,怎么求dp[4]呢? - 返回值
根据状态表示,我们知道返回i位置的元素就是第i个泰波那契数,所以我们直接返回i即可
现在我们就可以开始上手写代码
写代码也是有几个步骤需要注意
class Solution {
public:int tribonacci(int n) {//创建dp表int* dp=new int[n+1];//边界问题if(n==0||n==1){return n;}if(n==2){return 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];}
};
- 观察是否可以优化

可以看出,我们内存消耗比较大,这是因为传入多少个n我们就开了n个空间,时间复杂度为O(N),空间复杂度也是O(N)。
这道题我们可以优化空间复杂度,我们在进行计算时,可以使用几个变量循环往复存储dp的值,这样只需要常熟级别的空间消耗就解决了这个问题。
如图

然后我们就可以得出第四个结果的值为2,此时更新a,b,c中的值

优化后代码如下
class Solution {
public:int tribonacci(int n) {//创建dp表int a,b,c,sum;//定义四个变量//边界问题if(n==0||n==1){return n;}if(n==2){return 1;}//初始化a=0;b=1;c=1;//填表for(int i=3;i<=n;i++){sum=a+b+c;a=b;b=c;c=sum;}return sum;}
};
这道题我们会提到空间优化,后续中关于空间优化我们就不再多说了,重要的是能通过这道题,对吧!
接下来就是一道我们再熟悉不过的一种题目了,上楼梯问题
来看第一道关于上楼梯的题目
三步问题

使用上边同样的分析步骤
- 状态表示
这道题目和上一道就已经截然不同了,我们需要自己进行分析,这道题目中,我们开出的dp表第i个位置的元素,表示的是到达第i位置台阶有多少种方法。
- 状态转移方程
小孩依次可以上1,2,3节台阶,我们可以一个一个进行分析,为什么这里状态表示是小孩到达该台阶所有的走法。

小明上第一节台阶,显而易见,只有一种方法,就是直接跳上去.
小明上第二个台阶时,我们就可以进行选择了,首先,我们可以先到第一个台阶,再从第一个台阶到第二个台阶,也可以直接到第二个台阶。
怎么上第三个台阶呢?我们可以先到第一个台阶,再从第一个台阶直接到第三个台阶,到第一个台阶有一种方法,也可以先到第二个台阶,然后从第二个台阶到第三个台阶,到达第二个台阶有两种方法,所以到达第3个台阶有1+1+2中方法。

从状态表示我们可以看出,小孩一次可以走三步,dp[i]就等于我们先到dp[i-1]的方法数量加上dp[i-2]加dp[i-3].
所以状态表示方程为dp[i]=dp[i-1]+dp[i-2]+dp[i-3]
3. 初始化
通过上边的分析,我们已经知道了爬前三节楼梯可以有的方法数目,不再赘述。
- 确定填表顺序
和前边拿到题目一样,填表顺序必须能帮助我们完成,在想要求出某个值时,所需要的元素都是已知的。 - 返回值
通过状态表示可以知道,返回dp[n]即返回到达第n节台阶时有多少种上楼梯的方法。
ok,有了上述的分析,我们就可以上手写代码了。
class Solution {
public:int waysToStep(int n) {//开dp表int* dp=new int[n+1];//边界调节if(n==1||n==2){return n;}if(n==3){return 4;}//初始化dp[1]=1;dp[2]=2;dp[3]=4;//填dp表for(int i=4;i<=n;i++){dp[i]=dp[i-1]+dp[i-2]+dp[i-3];}return dp[n];}
};
运行后

这个报错的含义就是计算结果超出了int的数据范围,这是因为我们忽略了题目中所说要对结果取模。

第三道题
使用最小花费爬楼梯

这道题目还是一个爬楼梯的题目,一次可以爬两节,和上到题目不同的是,我们不需要计算方法数,而是在爬每节楼梯索要花费的最小花费,用同样的方法对象这道题目进行解析。
但是这次我们可以选择从两个方面对这道题目进行分析
- 第一种
- 状态表示
有了上边的经验,这道题目我们同样可以知道,同样创建一个dp表,dp[i]表示到达i台阶花费的最少的money。
2. 状态转移方程
我们一步一步进行分析,首先我们要知道cost第一个元素为台阶是0的台阶要花费的money,我们可以选择从第0个或者第1个台阶开始往上。 我们要想知道到达i位置所花费的最小数目,要到达第i阶只有两种方法,就是从第i-1位置或者第i-2位置往上跑过去的,所以我们要知道dp[i-1]加上第i-1位置的费用,还要知道dp[i-2]加上i-2台阶的费用。
所以我们的撞他转移方程为
dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
- 初始化
初始化保证填表的时候不会越界。我们要到楼梯顶部,而cost数组是从0下标开始的,所以我们所要到达的楼顶是dp[n],n就是cost的大小。
这道题为什么没有边界判断呢?
这是因为数据范围是大于等于2的。我们起始就在0或者1号台阶上,所以dp[0]=0,dp[1]=0. - 确定填表顺序
我们想要知道爬到后边台阶花费的最少费用,就需要知道爬到他前边两个台阶所花费的最少费用加上从这个台阶向上爬所需要的费用。所以填表顺序是从左往右。 - 返回值
返回值就是dp[i],i就是楼梯顶部需要的最低花费。要注意,顶楼并不是cost数组最后一个位置,cost最后的元素表示距离楼顶前的台阶往上爬所要消费的money。
ok,接下来我们就可以上手写代码了。流程基本不变。
class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int* dp=new int[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()];}
};
接下来我们来换一种思路去求解这道题。
-
状态表示
dp[i]表示从i位置出发,到达楼顶需要的最少花费。和前边的思路不同的是,我们这次从前向后递进。

-
状态转移方程

很明显,我们应该选出两者之中小的那个,正如状态表示所说,dp[i]表示的是从该位置到达楼顶的最小花费。如果从i+1位置到达楼顶花费比从i+2位置到达楼顶花费少的话,我们当然要选择从i+1位置到达楼顶。
状态转移方程为
dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i])
-
初始化
此时,根据状态方程,我们应该从右向左初始化,即初始化后边的值,dp表示从某位置出发到达楼顶位置,假设我们初始化的dp表为dp[n]。

其中这里的n是cost.size()。表示的就是楼顶的位置,楼顶我们就不用初始化了,不管怎样都是0,dp[n-1]就是cost[cost.size()-1]。 -
确定填表顺序
填表顺序就是保证想要知道某位置的元素时,所需要的计算过程中的元素是已知的,所以这道题就是从右向左填表。 -
返回值
根据状态表示可知从i=0位置到达楼顶的花费和i=1位置到达楼顶的花费的较小值。
代码如下
int minCostClimbingStairs(vector<int>& cost) {int* dp=new int[cost.size()+1];int n=cost.size();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]);}
相关文章:
算法·动态规划Dynamic Programming
很多人听到动态规划或者什么dp数组了,或者是做到一道关于动态规划的题目时,就会有一种他很难且不好解决的恐惧心理,但是如果我们从基础的题目开始深入挖掘动规思想,在后边遇到动态规划的难题时就迎难而解了。 其实不然ÿ…...
鸿蒙Harmony应用开发—ArkTS-转场动画(共享元素转场)
当路由进行切换时,可以通过设置组件的 sharedTransition 属性将该元素标记为共享元素并设置对应的共享元素转场动效。 说明: 从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 属性 名称参数参数描述…...
【C语言】循环语句(语句使用建议)
文章目录 **while循环****while循环的实践****补充:if语句与while语句区别****for循环(使用频率最高)****for循环的实践****while循环和for循环的对比****Do-while循环****break和continue语句****循环的嵌套****goto语句(不常用)****循环语句的效率(来自于高质量的C/C编程书籍…...
Spring Data访问Elasticsearch----响应式Reactive存储库
Spring Data访问Elasticsearch----响应式Reactive存储库 一、用法二、配置 Reactive Elasticsearch存储库支持建立在存储库中解释的核心存储库支持之上,利用由 Reactive REST客户端执行的 Reactive Elasticsearch Operations提供的操作。 Spring Data Elasticsear…...
堆排序(c语言)
文章目录 前言一.什么是堆二.向下调整算法三.堆排序的创建总结 前言 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于&#x…...
开源IT自动化运维工具Ansible解析
Ansible 是一款开源的 IT 自动化工具,用于简化应用程序部署、配置管理、持续集成、基础设施即代码(Infrastructure as Code, IaC)和服务编排。它由 Michael DeHaan 创建,并在2012年首次发布,到2015年被红帽公司&#x…...
【C++】仿函数优先级队列反向迭代器
目录 一、优先级队列 1、priority_queue 的介绍 2、priority_queue 的使用 3、 priority_queue 的模拟实现 1)priority_queue()/priority_queue(first, last) 2)push(x) 3)pop() 4&#…...
UE4_调试工具_绘制调试球体
学习笔记,仅供参考! 效果: 步骤: 睁开眼睛就是该变量在此蓝图的实例上可公开编辑。 勾选效果:...
机器人路径规划:基于冠豪猪优化算法(Crested Porcupine Optimizer,CPO)的机器人路径规划(提供MATLAB代码)
一、机器人路径规划介绍 移动机器人(Mobile robot,MR)的路径规划是 移动机器人研究的重要分支之,是对其进行控制的基础。根据环境信息的已知程度不同,路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或…...
探索.NET中的定时器:选择最适合你的应用场景
概述:.NET提供多种定时器,如 System.Windows.Forms.Timer适用于UI,System.Web.UI.Timer用于Web,System.Diagnostics.Timer用于性能监控,System.Threading.Timer和System.Timers.Timer用于一般定时任务。在.NET 6及以上…...
5467: 【搜索】流浪奶牛
题目描述 吃不到饭的奶牛Bessie一气之下决定离开农场,前往阿尔费茨山脉脚底下的农场(听说那儿的草极其美味)投靠她的亲戚Jimmy。但是前往目的地的山路崎岖,Bessie又没有吃饭,她需要尽量保存体力,以最轻松的…...
spring boot整合elasticsearch实现查询功能
第一步、添加依赖(注意版本对应关系)根据spring boot版本选择合适的版本 <dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.6.2</version></dependenc…...
白嫖阿里云程序员日历
https://developer.aliyun.com/topic/lingma/activities/202403?taskCode14508&recordId44f3187f7950776f494eec668a62c65f#/?utm_contentm_fission_1 「通义灵码 体验 AI 编码,开 AI 盲盒」 打开链接直接领就行了...
ubuntu20.04搭建rtmp视频服务
1.安装软件 sudo apt-get install ffmpeg sudo apt-get install nginx sudo apt-get install libnginx-mod-rtmp 2.nginx配置 修改/etc/nginx/nginx.conf文件,在末尾添加: rtmp {server {listen 1935;application live {live on;}} } 3.视频测试 本…...
Request failed with status code 504,Gateway time out
问题描述: 部署在测试环境的项目在执行某功能时,后台程序在执行过程中,前端控制台在一分钟左右会报出Request failed with status code 504,Gateway time out异常。但是在本地开发环境会正常运行,并不会报出异常。 问题…...
四、Elasticsearch 进阶
自定义目录 4.1 核心概念4.1.1 索引(Index)4.1.2 类型(Type)4.1.3 文档(Document)4.1.3 字段(Field)4.1.5 映射(Mapping)4.1.6 分片(Shards&#…...
海外云手机如何帮助亚马逊引流?
随着全球化的推进,出海企业和B2B外贸企业越来越注重海外市场的开拓,这已成为企业争夺市场份额的重要策略。本文将重点探讨海外云手机在优化亚马逊店铺引流方面的作用和优势。 海外云手机是一种在云端运行的虚拟手机,能够在单一芯片上多开几个…...
Gateway新一代网关
Gateway新一代网关 1、概述 Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关; 但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul。 官网&…...
Simulink中Scope图像导出在MATLAB上重新画
在Simulink中,Scope是一个常用的可视化工具,用于实时显示仿真过程中的信号波形。 1. 从Simulink Scope中导出数据 首先,您需要在Simulink的Scope中捕获或记录想要导出的数据。这通常通过配置Scope的“Logging”选项来实现。确保在仿真过程中…...
利用opencv获取系统时间
前一篇《c获取系统时间的方法-CSDN博客》博客介绍了如何在不同系统中获取系统时间的方法,但这些方法受系统的限制,如time.h就只能在Linux系统中使用。而opencv则不受系统限制,示例代码如下, #include <opencv2/opencv.hpp>…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
