算法·动态规划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位置的值。
我们可以用动态规划的分析方法来分析这道题目

  1. 状态表示
    我们可以创建一个数组
    在这里插入图片描述
     这个数组就叫做dp表,我们所要做的就是把dp表中的数填满,然后就可以得到我们的最终结果。
     那么状态表示是什么意思呢?
     状态表示就是dp[i]位置上元素的含义,dp[i]在这道题目中就表示第i个泰波那契数的值。
     这道题目的状态表示很容易就能看出来,但遇到难题后,会遇到其他状态表示的方式,有一些会比较抽象,但是我们可以从浅入深,慢慢来嘛。

  2. 状态转移方程
     这道题目的状态转移方程题目已经给出来咯,如果后边遇到某些题没给呢?那当然是自己找规律,很简单的。

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

  1. 初始化
     如果知道了状态转移方程,然而不知道如何求出dp数组中的元素,那不就是没用嘛,所以前边给出我们前三个状态的值,由此我们才可以知道第四个,第五个的值。
     所以初始化还是很重要的,根据题目所示,将我们构建出的数组进行初始化。
  2. 填表顺序
     这道题的填表顺序明显是从左到右,知道了前边数组的元素,我们才能根据状态转移方程来求解右边的值。
    你是怎么知道填表顺序的呢?
     确定填表顺序,为了填写当前状态表示的时候,所需要的状态已经计算过了。就像这道题,我们要求dp[4]的值,然而dp[3]我们都不知道,怎么求dp[4]呢?
  3. 返回值
     根据状态表示,我们知道返回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];}
};
  1. 观察是否可以优化
    在这里插入图片描述

 可以看出,我们内存消耗比较大,这是因为传入多少个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;}
};

 这道题我们会提到空间优化,后续中关于空间优化我们就不再多说了,重要的是能通过这道题,对吧!
接下来就是一道我们再熟悉不过的一种题目了,上楼梯问题
来看第一道关于上楼梯的题目
三步问题

在这里插入图片描述
使用上边同样的分析步骤

  1. 状态表示

 这道题目和上一道就已经截然不同了,我们需要自己进行分析,这道题目中,我们开出的dp表第i个位置的元素,表示的是到达第i位置台阶有多少种方法。

  1. 状态转移方程

 小孩依次可以上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. 初始化
 通过上边的分析,我们已经知道了爬前三节楼梯可以有的方法数目,不再赘述。

  1. 确定填表顺序
     和前边拿到题目一样,填表顺序必须能帮助我们完成,在想要求出某个值时,所需要的元素都是已知的。
  2. 返回值
     通过状态表示可以知道,返回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的数据范围,这是因为我们忽略了题目中所说要对结果取模。
在这里插入图片描述
第三道题
使用最小花费爬楼梯
在这里插入图片描述

 这道题目还是一个爬楼梯的题目,一次可以爬两节,和上到题目不同的是,我们不需要计算方法数,而是在爬每节楼梯索要花费的最小花费,用同样的方法对象这道题目进行解析。
 但是这次我们可以选择从两个方面对这道题目进行分析

  • 第一种
  1. 状态表示

 有了上边的经验,这道题目我们同样可以知道,同样创建一个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])

  1. 初始化
     初始化保证填表的时候不会越界。我们要到楼梯顶部,而cost数组是从0下标开始的,所以我们所要到达的楼顶是dp[n],n就是cost的大小。
    这道题为什么没有边界判断呢?
     这是因为数据范围是大于等于2的。我们起始就在0或者1号台阶上,所以dp[0]=0,dp[1]=0.
  2. 确定填表顺序
     我们想要知道爬到后边台阶花费的最少费用,就需要知道爬到他前边两个台阶所花费的最少费用加上从这个台阶向上爬所需要的费用。所以填表顺序是从左往右。
  3. 返回值
     返回值就是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()];}
};

接下来我们来换一种思路去求解这道题。

  1. 状态表示
    dp[i]表示从i位置出发,到达楼顶需要的最少花费。和前边的思路不同的是,我们这次从前向后递进。
    在这里插入图片描述

  2. 状态转移方程
    在这里插入图片描述
     很明显,我们应该选出两者之中小的那个,正如状态表示所说,dp[i]表示的是从该位置到达楼顶的最小花费。如果从i+1位置到达楼顶花费比从i+2位置到达楼顶花费少的话,我们当然要选择从i+1位置到达楼顶。
    状态转移方程为

dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i])

  1. 初始化
     此时,根据状态方程,我们应该从右向左初始化,即初始化后边的值,dp表示从某位置出发到达楼顶位置,假设我们初始化的dp表为dp[n]。
    在这里插入图片描述
     其中这里的n是cost.size()。表示的就是楼顶的位置,楼顶我们就不用初始化了,不管怎样都是0,dp[n-1]就是cost[cost.size()-1]。

  2. 确定填表顺序
     填表顺序就是保证想要知道某位置的元素时,所需要的计算过程中的元素是已知的,所以这道题就是从右向左填表。

  3. 返回值
     根据状态表示可知从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]);}

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

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

相关文章

linux:线程互斥

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、线程互斥问题解释互斥量的接口 二、加锁的原理三、 死锁死锁四个必要条件避免死锁 总结 前言 本文是对于线程互斥的知识总结 一、线程互斥 问题 我们先看下面…

财报解读:出海“窗口期”再现,汇量科技保驾护航的底气源于什么

大数据时代&#xff0c;每个人的喜好都被精准捕捉。购物APP、购物网站们&#xff0c;都仿佛一位贴心的时尚顾问。而这源于个性化广告经过深度学习和智能算法得来的结果。 随着广告市场的竞争愈演愈烈&#xff0c;广告主们需要更为精准、高效的个性化投放。近日&#xff0c;深耕…

基于SSM的宿舍管理系统的设计与实现(JSP,MySQL)

摘 要 随着社会发展、信息技术的普及&#xff0c;人们日常管理工作也发生了巨大的变化。信息化技术之渗透各行业的方方面面。学生宿舍管理作为校园管理工作的重要一环&#xff0c;不仅关系到学生自身的确切利益&#xff0c;同时也是对校园管理工作重大考验。近来年由于在校学生…

leetcode代码记录(移除链表元素

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head […

Flutter开发进阶之瞧瞧Widget

Flutter开发进阶之瞧瞧Widget 在Flutter开发中,WIdget是构建界面的基本单元;Widget是不可变的,意味着一旦创建如果需要改变UI就需要重新创建一个新的Widget;在实际开发中,Widget通常由一个个Widget组合而成,从而形成嵌套的树形结构,复杂的UI就是由这一个个Widget构建而…

【C语言】—— 指针三 : 参透数组传参的本质

【C语言】—— 指针三 &#xff1a; 参透数组传参的本质 一、数组名的理解二、使用指针访问数组2.1、指针访问数组2.2、[ ] 的深入理解2.3、数组与指针的区别 三、一维数组的传参本质四、数组指针变量4.1、数组指针变量是什么4.2、 数组指针的初始化 五、二维数组传参的本质 一…

简单了解多线程

并发和并行 并发&#xff1a; 在同一时刻&#xff0c;多个指令在单一CPU上交替指向 并行&#xff1a;在同一时刻&#xff0c;多个指令在多个CPU上同时执行 2核4线程&#xff0c;4核8线程&#xff0c;8核16线程&#xff0c;16核32线程 基础实现线程的方式 Thread :继承类 &…

多人命题系统|基于SSM框架+ Mysql+Java+ B/S结构的多人命题系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

13年资深测试,性能测试常见指标分析总结,看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、什么是性能测试…

Kotlin runBlocking CoroutineScope synchronized简单死锁场景

Kotlin runBlocking CoroutineScope synchronized简单死锁场景 import kotlinx.coroutines.*fun main(args: Array<String>) {runBlocking {val lock1 Any()val lock2 Any()CoroutineScope(Dispatchers.IO).launch {repeat(10) {println("A-$it 申请 lock1...&quo…

Http 超文本传输协议基本概念学习摘录

目录 HTTP协议 超文本传输协议 HyperText超文本 HTML超文本标记语言 HTTP协议原理 请求发送 服务器处理 响应发送 连接关闭或保持 HTTP协议版本 HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/3 HTTP请求方法 GET POST PUT DELETE HEAD OPTIONS HTTP请求头字…

JVM学习-类加载

目录 1.类文件结构 2.类加载器 3.类加载的三个阶段 3.1加载 3.2链接 3.2.1验证 3.2.2准备阶段 3.2.3解析阶段 3.3初始化 4.拓展&#xff1a;反射 4.1获取类对象 4.2创建实例 4.3获取方法 4.4方法调用 1.类文件结构 2.类加载器 类加载器用来将类文件的二进制字节码加载到JV…

猜数字游戏有三变(Java篇)

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

小白学视觉 | 超详细!Python中 pip 常用命令

本文来源公众号“小白学视觉”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;超详细&#xff01;Python中 pip 常用命令 相信对于大多数熟悉Python的人来说&#xff0c;一定都听说并且使用过pip这个工具&#xff0c;但是对它的了…

中国联通推出了一站式全流程的专业安全服务

中国联通依托多年深耕通信与信息安全领域的丰富经验和专业技术积累&#xff0c;推出了一站式全流程的专业安全服务&#xff0c;从网络环境的前期准备阶段直至正式运行&#xff0c;均提供全面、立体、高效的保障措施&#xff0c;确保各类企事业单位在网络空间的安全稳定。 请点击…

算法打卡day23|回溯法篇03|Leetcode 39. 组合总和、40.组合总和II、131.分割回文串

算法题 Leetcode 39. 组合总和 题目链接:39. 组合总和 大佬视频讲解&#xff1a;组合总和视频讲解 个人思路 这道组合题主要是有总和的限制&#xff0c;当递归和超过了总和就return&#xff0c;递归时加上回溯去遍历数组。 解法 回溯法 把组合问题抽象为如下树形结构 如上…

达源电机超高速数码马达震撼来袭

新质生产力是什么? 12万转高速电吹风机马达引领行业技术革新 随着科技的不断进步&#xff0c;电吹风机行业正迎来一场深刻新质生产力技术革新。在这场革新中&#xff0c;达源电机以其独特绕线技术与自适应平衡磁场的马达技术&#xff0c;成功打破了国外高速马达电机悬臂梁专利…

uniapp——第3篇:自定义组件、组件间传数据

前提&#xff0c;建议先学会前端几大基础&#xff1a;HTML、CSS、JS、Ajax&#xff0c;还有一定要会Vue!&#xff08;Vue2\Vue3&#xff09;都要会&#xff01;&#xff01;&#xff01;不然不好懂 一、组件是啥玩意&#xff1f; 我之前讲vue2的文章讲过 Vue全家桶:vue2vue3全…

位图与布隆过滤器

目录 一、位图 1、问题用位图来解决&#xff1a; 二、 布隆过滤器 1、将哈希与位图结合&#xff0c;即布隆过滤器 2.布隆过滤器的查找 3.布隆过滤器的删除 4.布隆过滤器优点 5、布隆过滤器缺陷 三、海量数据处理问题&#xff1a; 一、位图 问题1&#xff1a;给40亿个不…

netron:本地查看服务器端打开的onnx文件

我们开发一般都在服务器中开发&#xff0c;假如我们在服务器端导出了一个onnx文件&#xff0c;不将onnx文件传到本地&#xff0c;如何进行本地查看呢&#xff1f; netron --port 8082 --host 10.75.29.201 model_data/deeplab_0131.onnx--host : 指定服务器的ip, 注意不是本地…