递归学习——记忆化搜索

目录

​编辑

一,概念和效果

二,题目

1.斐波那契数

1.题目

2.题目接口

3.解题思路

2.不同的路径

1.题目

2.题目接口

3.解题思路

3.最长增长子序列

1.题目

2.题目接口

3.解题思路

4.猜数字游戏II

1.题目

2.题目接口

3.解题思路

总结:


一,概念和效果

记忆化搜索可以说是带备忘录的递归实现这个算法的目的便是减少递归时对同一个节点的多次遍历从而提高学习效率。学习这个算法,理解这个算法最好的方式便是通过能够用记忆化搜索的题目来理解。

二,题目

1.斐波那契数

1.题目

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n) 。

2.题目接口

class Solution {
public:int fib(int n) {}
};

3.解题思路

相信大家都知道如何解决斐波那契数的问题。这个问题的经典解决方式便是运用到递归解法。使用递归时要明确的便是递归的出口。斐波那契数的递归出口便是在n == 0或者n==1时。这两个条件下的返回值便是它们本身。当n不等于0和1时f(n) = f(n-1)+f(n-2)条件成立。

根据以上思路便可以写出如下解题代码:

class Solution {
public:int fib(int n) {return dfs(n);}int dfs(int n){if(n == 0||n == 1){return n;}return dfs(n-1)+dfs(n-2);}
};

但是我们都知道在递归时会有大量的重复计算。比如当n == 5时递归展开图如下:

在这里可以看到2这个节点被求了很多次,这样子便是很大的浪费了。这个时候为了提高效率便可以采用记忆化搜索的方式。记忆化搜索的实现其实也非常的简单,也就是当我们得到一个结果时便将其记录下来。当我们想要再次遍历相同的节点时只要看前面是否有记录过,若记录过便不再访问直接返回之前记录过的结果就行了。比如斐波那契数列这道题的记忆化搜索方式的解题代码如下:

class Solution {
public:vector<int>memo;//表示备忘录int fib(int n) {memo = vector<int>(n+1);//初始化return dfs(n);}int dfs(int n){if(n == 0||n == 1){return n;}if(memo[n]!=0)//册中已求便无需再求{return memo[n];}memo[n] = dfs(n-1)+dfs(n-2);//记录在册return memo[n];}
};

2.不同的路径

1.题目

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

2.题目接口

class Solution {
public:int uniquePaths(int m, int n) {}
};

3.解题思路

因为机器人只能向前或者向下走,所以要到达finish位置的话首先就要到达finish的上面和左边这两个位置:

要到达这两个位置的话便又要到达这两个位置的上面和下面位置。以次类推得到公式:

f(finishi,finishj) = f(finishi-1,finishj)+f(finishi,finishj-1)。得到这个关系我们便可以知道这道题和斐波那契数列有的一拼,所以自然就会想到递归的解法。在这里便要寻找递归条件了。

1.因为目中给的m与n表示的是网格的长与宽。所以当m == 1&&n == 1时就意味着网格里面只有一个格子,也就是机器人就在右下角的格子了所以返回1。

2.当给的m与n中有一个为0的话,也就是网格的长或者宽为0,也就表示没有格子于是返回0。

根据以上分析写出代码如下:

class Solution {
public:int uniquePaths(int m, int n) {return dfs(m,n);} int dfs(int m,int n){if(m == 1&&n==1){return 1;}if(m ==0||n==0){return 0;}return dfs(m-1,n)+dfs(m,n-1);}
};

但是这个代码会超时,所以我们得优化这个代码让这个代码变得更快。优化的方式便是记忆化搜索:

class Solution {
public:vector<vector<int>>memo;int uniquePaths(int m, int n) {memo = vector<vector<int>>(m+1,vector<int>(n+1));return dfs(m,n);} int dfs(int m,int n){if(memo[m][n]!=0){return memo[m][n];}if(m == 1&&n==1){return 1;}if(m ==0||n==0){return 0;}memo[m][n] = dfs(m-1,n)+dfs(m,n-1);return  memo[m][n];}
};

可以看到这道题的记忆化搜索处理方式和上一道题一毛一样。

3.最长增长子序列

1.题目

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

2.题目接口

class Solution {
public:int lengthOfLIS(vector<int>& nums) {}
};

3.解题思路

这道题的解题思路其实也不太难,就是要遍历一下每一个下标然后求出以每一个下标的起点为开始位置的子序列长度。但是因为我们要求的是最大长度所以需要比较更新最大长度。以这个思路写成代码如下:

class Solution {
public:int len; int lengthOfLIS(vector<int>& nums) {len = nums.size();int ret = 0;for(int i = 0;i<len;i++){ret = max(ret,dfs(i,nums));//得到以某个下标为起点的最大长度} return ret;}int dfs(int i,vector<int>&nums){int ret = 1;//每一个子序列的最小长度为1for(int x =i+1;x<len;x++ ){if(nums[x]>nums[i]){ret = max(ret,dfs(x,nums)+1);//求出以每一个下标为起点的子序列长度,并每次都更新一下}}return ret;//返回最大值}
};

但是以上代码是通不过的,因为时间限制:

那该怎么办呢?其实很简单,还是和前两道题一样要采用一个记忆化搜索的策略:

class Solution {
public:int len; vector<int>memo;int lengthOfLIS(vector<int>& nums) {len = nums.size();memo = vector<int>(len,1);int ret = 0;for(int i = 0;i<len;i++){ret = max(ret,dfs(i,nums));} return ret;}int dfs(int i,vector<int>&nums){if(memo[i]!=1)//判断一下{return memo[i];}int ret = 1;for(int x =i+1;x<len;x++ ){if(nums[x]>nums[i]){ret = max(ret,dfs(x,nums)+1);}}memo[i] = ret;//记录一下return ret;}
};

然后便过掉了:

4.猜数字游戏II

1.题目

我们正在玩一个猜数游戏,游戏规则如下:

  1. 我从 1 到 n 之间选择一个数字。
  2. 你来猜我选了哪个数字。
  3. 如果你猜到正确的数字,就会 赢得游戏 。
  4. 如果你猜错了,那么我会告诉你,我选的数字比你的 更大或者更小 ,并且你需要继续猜数。
  5. 每当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。如果你花光了钱,就会 输掉游戏 。

给你一个特定的数字 n ,返回能够 确保你获胜 的最小现金数,不管我选择那个数字 。

2.题目接口

class Solution {
public:int getMoneyAmount(int n) {}
};

3.解题思路

这道题该咋做呢?或者说这道题是什么意思呢?以输入一个数字10为例吧。我们要猜数字时便要在[1,10]之间猜测。于是我们猜数字策略便有很多种。比如一下几种:

1.当开始位置为1时

这里的至少要1+2+3+4+5+6+7+8块钱。

2.当我们一开始便选到5时:

还有一种便是这道题目给的最优解法:

在这个最优解法里边我们要做的便是找到这里的最大钱数也就是7+9 = 16。所以我们要做的便暴力搜索找出这个最优策略里的最大钱数。怎么做呢?其实还是遍历,抽象成下图:

这里一个一个试验的i便是为了得到最优模型而设计的。返回最大值便是为了得到每一个模型里面的最坏情况然后让每一个情况比较一下得到最优模型的最坏情况。写成代码如下:

class Solution {
public:int getMoneyAmount(int n) {return dfs(1,n);}int dfs(int left,int right){if(left>=right)//当数组范围中只有一个数字或者范围不合法时便返回0。{return 0;}int ret = INT_MAX;//记录最优模型的最坏情况。for(int head = left;head<right;head++){int l = dfs(left,head-1)+head;//左结果int r = dfs(head+1,right)+head;//右结果ret = min(ret,max(l,r));//最优模型下的追怀情况}return ret;}
};

这样便得到了代码了,这个代码是对的但是遗憾的是这个代码过不了:

接下来采用记忆化搜索方式:

class Solution {
public:vector<vector<int>>memo;int getMoneyAmount(int n) {memo = vector<vector<int>>(n+1,vector<int>(n+1));return dfs(1,n);}int dfs(int left,int right){if(memo[left][right]!=0){return memo[left][right];}if(left>=right){return 0;}int ret = INT_MAX;for(int head = left;head<right;head++){int l = dfs(left,head-1)+head;int r = dfs(head+1,right)+head;ret = min(ret,max(l,r));}memo[left][right] = ret;return ret;}
};

这样便可以过掉了:

总结:

其实记忆化搜索的目的便是实现剪枝操作,提高递归效率。当我们的递归操作里有大量的重复的递归操作时便可以用记忆化搜索的方式来提高递归效率。

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

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

相关文章

使用 Sealos 一键部署高可用 MinIO,开启对象存储之旅

大家好&#xff01;今天这篇文章主要向大家介绍如何通过 Sealos 一键部署高可用 MinIO 集群。 MinIO 对象存储是什么&#xff1f; 对象是二进制数据&#xff0c;例如图像、音频文件、电子表格甚至二进制可执行代码。对象的大小可以从几 B 到几 TB 不等。像 MinIO 这样的对象存储…

解决 Axios 跨域问题,轻松实现接口调用

跨域是指访问另外一个域的资源&#xff0c;由于浏览器的同源策略&#xff0c;默认情况下使用 XMLHttpRequest 和 Fetch 请求时是不允许跨域的。跨域的根本原因是浏览器的同源策略&#xff0c;这是由浏览器对 JavaScript 施加的安全限制。 Axios 跨域常见报错 跨域请求被阻止 (…

vue中 字体图标引入 - iconfont阿里字体图标库

官网&#xff1a;iconfont-阿里巴巴矢量图标库 代码应用中&#xff0c;有许多方法&#xff0c;如何使用该图标库。如&#xff0c;icon单个使用、unicode引用、或 font-class引用&#xff08;推&#xff09;、symbol&#xff08;svg合集&#xff09;。本文主讲 font-class 方法…

Git(9)——Git多人协同开发之创建初始项目

目录 一、简介 二、创建新项目 三、本地Git接入 四、创建远端仓库——Gitee 五、推送代码至远端仓库 一、简介 前面8章都是我们一个人独自开发&#xff0c;如果现在项目新增两名同事&#xff0c;我们就需要使用Git来实现多人协同开发&#xff0c;从第九章开始将介绍从零到…

Jenkins教程—构建多分支流水线项目

本教程向你展示如何使用Jenkins协调一个用 Node Package Manager (npm) 管理的简单 Node.js 和 React 项目&#xff0c; 并同时 为开发和产品环境交付不同的结果。 在开始本教程之前&#xff0c;建议你前往 教程概览 页面&#xff0c;并至少完成一个 介绍教程&#xff0c; 从而…

利用红黑树封装map和set

目录 一、正向迭代器1.1 operator1.2 operator--1.3 参考代码 二、反向迭代器三、封装set四、封装map五、底层红黑树的实现 一、正向迭代器 我们之前vector&#xff0c;list这些都是容器的迭代器都是简单的指针或者_node_node->next这样的&#xff0c;那是因为它们要么是连…

背包问题---怎么选取物品,可以使得背包装的物品价值最大?

原文&#xff1a; https://zhuanlan.zhihu.com/p/567560364 1&#xff09;0-1背包问题的描述 现在有四种物品&#xff0c;每种物品只有1件&#xff0c;它们的重量与价值如下表。 现在有一个背包&#xff0c;总容量为8。问怎么选取物品&#xff0c;可以使得背包装的物品价值…

yolov5添加ECA注意力机制

ECA注意力机制简介 论文题目&#xff1a;ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks 论文地址&#xff1a;here 基本原理 &#x1f438; ECANet的核心思想是提出了一种不降维的局部跨通道交互策略&#xff0c;有效避免了降维对于通道注意…

【力扣每日一题】2023.9.12 课程表Ⅳ

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 今天是课程表系列题目的最后一题&#xff0c;因为我在题库里找不到课程表5了&#xff0c;所以今天的每日一题就是最后一个课程表了。 题…

关于Arcgis这62个常用技巧,你知道几个?

小编推荐&#xff1a;ArcGIS类全领域教程推荐https://mp.weixin.qq.com/s?__bizMzU0MDQ3MDk3NA&mid2247560279&idx5&sn8ac52cabeb8c7e3b2c083e07ef6056da&chksmfb3b1786cc4c9e909bab16dd99e88e5f5f9816eb2349d6d73a68a137a5264aa606b2035d7b3e&token16747…

c++ 中的函数指针

以下图片演示了c中函数指针的用法。如下图可见&#xff0c;把函数地址赋值给函数指针&#xff0c;用函数名或者函数名的地址&#xff0c;都可以&#xff0c;c编译器不报错。即 ptr f 和 ptr &f 都对。但准确的话&#xff0c;函数名就是地址&#xff0c;在编译时候&#x…

2023-9-14 最长公共子序列

题目链接&#xff1a;最长公共子序列 #include <iostream> #include <algorithm>using namespace std;const int N 1010;int n, m; char a[N], b[N]; int f[N][N];int main() {cin >> n >> m;cin >> a 1 >> b 1;for(int i 1; i < n…

代理HTTP使用不当会出现哪些问题?如何正确使用代理服务?

代理HTTP是一种常见的网络代理方式&#xff0c;它为客户端和服务器之间提供中间层&#xff0c;转发上下游的请求和响应。正确使用代理HTTP可以提高采集效率、增加网络安全性、加速网络速度、保护用户隐私。但是&#xff0c;使用不当就难以达到预期的效果&#xff0c;在使用代理…

langchain+GPT+neo4j 图数据库

neo4j版本是5.11.0,langchain的版本 0.0.288下载apoc插件 https://neo4j.com/docs/apoc/current/installation/ neo4j.conf文件把apoc.*添加到dbms.security.procedures.unrestricted配置项 使用return apoc.version()来查看是否安装成功 pip install neo4j图 参考官网&…

vue中预览xml并高亮显示

项目中有需要将接口返回的数据流显示出来&#xff0c;并高亮显示&#xff1b; 1.后端接口返回blob,类型为xml,如图 2.页面中使用pre code标签&#xff1a; <pre v-if"showXML"><code class"language-xml">{{xml}}</code></pre> …

Spring源码分析(三) bean的生命周期 getBean()和doGetBean()

b、在中篇会正式经历一套生命周期流程 getBean() -> doGetBean() -> createBean() -> doCreateBean() -> createBeanInstance() -> populateBean() -> initializeBean() 流程 1、AbstractBeanFactory#getBean() 此处开始进行对象的获取也就是核心步骤 Overr…

shell脚本学习笔记02(小滴课堂)

可以在home目录下创建一个shell.sh文件。 按w进入命令行模式。按i进入插入模式。如果想返回命令行模式&#xff0c;按esc即可。然后可以使用x和dd进行删除内容。 在插入模式下我们点击esc键&#xff0c;再去按:键&#xff0c;我们就可以进入到底行模式了&#xff1a; 可以设…

PHP实现微信小程序状态检测(违规、暂停服务、维护中、正在修复)

实现原理 进入那些状态不正常的小程序会被重定向至一个Url&#xff0c;使用抓包软件抓取这个Url&#xff0c;剔除不必要参数&#xff0c;使用cURl函数请求网页获得HTML内容&#xff0c;根据内容解析出当前APPID的小程序的状态。 代码 <?php// 编码header(Content-type:ap…

数据库被攻击需要注意什么

没想到自己用了一个简单的腾讯虚拟机&#xff0c;里面自己安装了一个 MySQL 数据库也会被黑客攻击。 一、问题现象 小程序访问不了&#xff0c;后台程序报数据库相关的错误。 查看数据库&#xff0c;发现数据库被篡改。 二、问题原因 1、为了可以远程能够访问数据库&#xf…

基于Qt4开发曲线绘制交互软件Plotter

目前市面上有很多曲线绘制软件,但其交互功能较差。比如,想要实现数据的交互,同步联动等,都需要大量繁琐的人工操作。所以讲想开发一款轻量级的曲线绘制交互软件。下面就以此为案例,记录一下基于Qt4的开发过程。 目录 1 需求 2 技术路线 3 开发流程 1 框架搭建 2 菜单…