【算法】DFS系列之 记忆化搜索

news2024/11/27 8:42:06

【ps】本篇有 5 道 leetcode OJ。 

目录

一、算法简介

二、相关例题

1)斐波那契数

.1- 题目解析

.2- 代码编写

2)不同路径

.1- 题目解析

.2- 代码编写

3)最长递增子序列

.1- 题目解析

.2- 代码编写

4)猜数字大小 II

.1- 题目解析

.2- 代码编写

5)矩阵中的最长递增路径

.1- 题目解析

.2- 代码编写


一、算法简介

        记忆化搜索是一种典型的空间换时间的思想,可以看成带备忘录的爆搜递归。

        搜索的低效在于没有能够很好地处理重叠子问题。在搜索过程中,会有很多重复计算,如果我们能记录一些状态的答案,就可以减少重复搜索量。动态规划虽然比较好地处理了重叠子问题,但是在有些拓扑关系比较复杂的题目面前,又显得无奈。记忆化搜索正是在这样的情况下产生的,它采用搜索的形式和动态规划中递推的思想将这两种方法有机地综合在一起,扬长避短,简单实用,在信息学中有着重要的作用。

        根据记忆化搜索的思想,它是解决重复计算,而不是重复生成,也就是说,这些搜索必须是在搜索扩展路径的过程中分步计算的题目,也就是“搜索答案与路径相关″的题目,而不能是搜索一个路径之后才能进行计算的题目,必须要分步计算,并且搜索过程中,一个搜索结果必须可以建立在同类型问题的结果上,也就是类似于动态规划解决的那种。

        记忆化搜索的典型应用场景是可能经过不同路径转移到相同状态的dfs问题。更明确地说,当我们需要在有层次结构的图(不是树,即当前层的不同节点可能转移到下一层的相同节点)中自上而下地进行 DFS 搜索时,我们一般可以通过记忆化搜索的技巧降低时间复杂度。

二、相关例题

1)斐波那契数

509. 斐波那契数

.1- 题目解析

        斐波那契数列可以用很多种方法实现,例如循环、递推、递归、动态规划(dp)、记忆化搜索、矩阵快速幂等,能运用很多知识也十分锻炼代码能力。

        所谓记忆化搜索,就是在进行递归时,存在完全相同的子问题,此时可以把该子问题的结果存在一个“备忘录”中(一般为一个全局的哈希表),避免后续重复求解该子问题,进而增加代码的运行效率。

         由递归和记忆化搜索,可以引申出另一个算法——动态规划(dp)。

.2- 代码编写

//循环方式
class Solution {
public:
    int fib(int n) {
        if (n < 2)
            return n;
        int fib1 = 0, fib2 = 0, ret = 1;
        for (int i = 2; i <= n; ++i)
        {
            fib1 = fib2;
            fib2 = ret;
            ret = fib1 + fib2;
        }
        return ret;
    }
};
//dfs方式(递归)
class Solution {
public:
    int fib(int n) {
        return dfs(n);
    }
 
    int dfs(int n)
    {
        if(n <= 1)
            return n;
        return dfs(n - 1) + dfs(n - 2);
    }
};
//记忆化搜索方式
class Solution {
    int mem[31];//备忘录,本质是一个哈希表,存的是可变参数和递归返回值之间的映射
public:
    int fib(int n) {
        memset(mem,-1,sizeof(mem)); //初始化备忘录
        return dfs(n);
    }
    int dfs(int n)
    {
        if(mem[n]!=-1) //查找一下备忘录,若已进入过该状态,就不再进入了,直接向上返回(相当于剪枝)
            return mem[n];
            
        if(n==0 || n==1)
        {
            mem[n]=n; //填备忘录
            return n;
        }

        mem[n]=dfs(n-1)+dfs(n-2);//填备忘录
        return mem[n];
    }
};
//dp方式
class Solution {
    int dp[31];
public:
    int fib(int n) {
        dp[0]=0,dp[1]=1;//初始化
        for(int i=2;i<=n;i++)//填表顺序
            dp[i]=dp[i-1]+dp[i-2];//状态转移方程
        return dp[n];//返回值
    }
};

2)不同路径

62. 不同路径

.1- 题目解析

        对于网格中的某一个格子,它左侧的格子和上方的格子均只需一步就能到达它,也就是说,对于左侧的格子,有多少种到达它左侧格子的方式就有多少到达它的方式,同理,对于上方的格子,有多少种到达它上方格子的方式也就有多少到达它的方式,那么,到达某一个格子有多少种方式,其实就是到达它左侧的格子和上方的格子的方式之和。

.2- 代码编写

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> mem(m+1,vector<int>(n+1));//防越界,合理地映射下标
        return dfs(m,n,mem);
    }
    int dfs(int i,int j,vector<vector<int>>& mem)
    {
        if(mem[i][j]!=0)return mem[i][j];//查备忘录剪枝

        if(i==0 || j==0)return 0;//边界上的格子
        if(i==1 && j==1)         //紧邻边界的开始出发的格子
        {
            mem[i][j]=1;
            return 1;
        }

        mem[i][j]=dfs(i-1,j,mem)+dfs(i,j-1,mem);//填备忘录
        return mem[i][j];
    }
};

3)最长递增子序列

300. 最长递增子序列

.1- 题目解析

        我们可以以一个元素作为起点,去 DFS 它之后的元素,如果它之后的元素能够和它形成递增序列,就统计这个序列的长度。

        显然在这个 DFS 的过程中会出现重复的子问题,那么我们就可以通过记忆化搜索的方式来保存重复子问题的结果。

.2- 代码编写

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        vector<int> mem(n);
        int ret=0;
        for(int i=0;i<nums.size();i++)//将每一个元素作为起点分别DFS
            ret=max(ret,dfs(i,nums,mem));
        return ret;
    }
    int dfs(int pos,vector<int>& nums,vector<int>& mem)
    {
        if(mem[pos]!=0)return mem[pos];

        int ret=1;//序列中至少有一个元素
        for(int i=pos+1;i<nums.size();i++)
        {
            if(nums[i]>nums[pos])
            {
                ret=max(ret,dfs(i,nums,mem)+1);
            }
        }
        mem[pos]=ret;
        return mem[pos];
    }
};

4)猜数字大小 II

375. 猜数字大小 II

.1- 题目解析

        1 到 n 之间的数可以构成一棵棵二叉搜索树,题目要求返回能够获胜的最小现金数,其实是要求某一数字作为根节点时,其左右子树中的最大值(返回左右子树的最大值,才能保证所有叶子节点是可选的,才不会输游戏),然后求出这些数字作为根节点时所得到的值中的最小值。

        显然在 DFS 一棵棵二叉搜索树时,存在重复的情况,我们可以用一个备忘录保存它们的结果来进行剪枝。

.2- 代码编写

class Solution {
    int memo[201][201];

public:
    int getMoneyAmount(int n) { return dfs(1, n); }
    int dfs(int left, int right) {
        if (left >= right)//递归出口
            return 0;
        if (memo[left][right] != 0)//查备忘录剪枝
            return memo[left][right];
        int ret = INT_MAX;
        for (int head = left; head <= right; head++) // 选择头结点
        {
            int x = dfs(left, head - 1);
            int y = dfs(head + 1, right);
            ret = min(ret, head + max(x, y));
        }
        memo[left][right] = ret;//填备忘录
        return ret;
    }
};

5)矩阵中的最长递增路径

329. 矩阵中的最长递增路径

.1- 题目解析

        遍历所有的方格,分别以它们为起点进行 DFS,找出最长递增路径即可。

        显然在 DFS 所有路径时,存在重复的情况,我们可以用一个备忘录保存它们的结果来进行剪枝。

.2- 代码编写

class Solution {
    int m, n;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int memo[201][201];

public:
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        int ret = 0;
        m = matrix.size(), n = matrix[0].size();
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++) {
                ret = max(ret, dfs(matrix, i, j));
            }

        return ret;
    }
    int dfs(vector<vector<int>>& matrix, int i, int j) {
        if (memo[i][j] != 0)
            return memo[i][j];
        int ret = 1;
        for (int k = 0; k < 4; k++) {
            int x = i + dx[k], y = j + dy[k];
            if (x >= 0 && x < m && y >= 0 && y < n &&
                matrix[x][y] > matrix[i][j]) {
                ret = max(ret, dfs(matrix, x, y) + 1);
            }
        }
        memo[i][j] = ret;
        return ret;
    }
};

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

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

相关文章

Linux云计算 |【第四阶段】RDBMS2-DAY5

主要内容&#xff1a; PXC概述、部署PXC&#xff08;自动故障恢复测试&#xff09;、存储引擎、读锁/写锁、表锁/行锁、常用的存储引擎介绍 一、PXC概述 PXC&#xff08;Percona XtraDB Cluster&#xff0c;简称PXC集群&#xff09;&#xff0c;是基于Galera的MySQL高可用集群…

OpenCV 之 实现基于Lucas-Kanade算法的光流追踪

引言 在计算机视觉中&#xff0c;光流是指物体、场景或摄像机之间的相对运动造成的像素变化。光流估计是计算机视觉中的一个基础课题&#xff0c;广泛应用于许多领域&#xff0c;比如自动驾驶汽车、机器人导航、手势识别等。OpenCV是一个开源的计算机视觉库&#xff0c;提供了…

vue使用gdal-async获取tif文件的缩略图

vue使用gdal-async获取tif文件的缩略图 npm i gdal-asyncgdal-async 在Vue组件中使用gdal-async获取缩略图&#xff1a; <template><div><img v-if"thumbnail" :src"thumbnail" alt"Thumbnail" /></div> </templa…

友思特方案 | FantoVision边缘计算:嵌入式视觉系统如何实现“更快 更高 更强”?

导读 便于集成的嵌入式视觉系统一直以来面临着带宽、内存、算力三个方面的挑战。友思特 FantoVision 边缘计算设备拥有更快的处理速度和更高的带宽选择&#xff0c;其开放式架构有效突破了上述三重阻碍。 嵌入式视觉 嵌入式视觉是传统机器视觉衍生出来的子集&#xff0c;嵌入…

STM32移植RT-Thread实现PWM波的输出

在进行PWM波的学习中&#xff0c;依然是踩了小坑&#xff0c;网络上大部分配置都增加了TIM的配置&#xff0c;貌似是不需要的&#xff0c;当开启了TIM的时钟反而影响了PWM的时钟&#xff0c;暂且归咎于版本不一样&#xff1f;还是STM32F1和STM32F4不一样呢&#xff1f;核心问题…

【黑马点评优化】之使用Caffeine+Redis实现应用级二层缓存

【黑马点评优化】之使用CaffeineRedis实现应用级二层缓存 1 缓存雪崩定义及解决方案2 为什么要使用多级缓存3 RedisCaffeine实现应用层二级缓存原理4 利用CaffeineRedis解决Redis突然宕机导致的缓存雪崩问题4.1 pom.xml文件引入相关依赖4.2 本地缓存配置类4.3 修改ShopServiceI…

Blazor Web APP学习记录

目录 1 呈现模式1.1 静态SSR1.2 服务器端交互SSR1.3 客户端交互CSR1.4 自动交互式1.5 注意 2 Blazor Web APP项目3 会话状态 1 呈现模式 参见&#xff1a;https://learn.microsoft.com/zh-cn/aspnet/core/blazor/components/render-modes?viewaspnetcore-8.0 Blazor Web App …

torch-npu的配置

1、简单的调用npu import torch import torch_npu x torch.randn(10000, 10000).npu() y torch.randn(10000, 10000).npu() for _ in range(10000):z x.mm(y) 这个代码的出现就会导致&#xff0c;核心使用99%&#xff0c;显存| 1633 2、安装torch-npu 安装方法看官网介绍…

npm使用时报错:Could not retrieve https://npm.taobao.org/mirrors/node/index.json.

在使用npm时报错&#xff0c;报错信息如下&#xff1a; 报错的原因&#xff1a;是原来的淘宝镜像地址过期了 解决办法&#xff1a;修改镜像地址。打开nvm的安装地址 -->settings.txt文件 -->配置下载源 1、将settings.txt文件中的 node_mirror: https://npm.taobao.or…

机器学习可解释性

机器学习的稳健性、可解释性和结果正确性等是人工智能安全可信应用必须解决的关键问题。 传统机器学习&#xff1a; 内置可解释性&#xff1a;决策树IF-Then规则&#xff0c;直观可理解事后可解释性&#xff1a;训练结束后的可解释技术特定于模型体系结构的解释与解释方法及模…

删除 Word 空白页的 3 种方法总结

在使用 Word 进行文档编辑的时候&#xff0c;空白页的出现常常会让用户感到困扰&#xff0c;Word 空白页的出现可能是由于分页、段落设置以及格式问题&#xff0c;空白页可能会出现在文档的开始、中间及结尾&#xff0c;如果需要打印文档还会影响打印效果&#xff0c;那么 Word…

【论文#性能对比】Video coding with H.264/AVC: Tools, Performance, and Complexity

目录 摘要1.前言2.标准化视频编码方案的概念3. H.264/AVC 编码方案3.1 帧内预测3.2 运动补偿预测3.3 变换编码3.4 熵编码方案3.5 自适应去块滤波器3.6 错误鲁棒性和网络友好性 4.码率受限的编码器控制5. H.264/AVC 的配置文件和级别6.与先前标准的比较6.1 编码效率6.2 硬件复杂…

如何启动一个OpenSearch

创建两个集群&#xff0c;标注 不含备用节点 选择集群版本和配置集群版本 冷热存储和专用主节点这个按需开启 然后是网络&#xff0c;是否使用自定义域名&#xff0c;集群开在VPC还是公网上。 选择是否开启认证&#xff1a; 访问策略&#xff0c;其实就是资源策略 维护时段…

vue页面使用v-print指令打印表格表单的几种方法,包括页眉标题自定义设置

页面效果: 打印预览页面: 步骤: 1.安装vue-print-nb 2.在main.js里面注册 import Print from vue-print-nbVue.use(Print) 3.在页面.vue代码中定义打印范围和打印按钮&#xff0c;我这里是以id"main-div"这个div为范围进行打印 <div> <div id"main-d…

grs认证有什么好处?grs认证相关政策

GRS认证&#xff08;Global Recycled Standard&#xff09;即全球回收标准认证&#xff0c;是一项国际、自愿和全面的产品标准&#xff0c;主要针对回收材料的使用和供应链进行认证。GRS认证能够为企业带来多方面的好处&#xff0c;以下是对这些好处的详细归纳&#xff1a; 一…

【livox雷达】HAP雷达 红外 orin 交换机 ——办公室 硬件搭配

orin&#xff1a;拉一根网线&#xff0c;一端连orin&#xff0c;一端连交换机 雷达&#xff1a;接上电源&#xff0c;另一端连接交换机 红外&#xff1a;用gsml方式连到orin上 交换机&#xff1a;以交换机为中心&#xff0c;连一根网线到orin&#xff0c;一根接外网的网线连给…

CesiumLab介绍

软考鸭小程序 学软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 CesiumLab是一个围绕Cesium平台设计的完整易用的数据预处理工具集&#xff0c;它旨在最大化提升三维数据可视化效率。本文将详细介绍CesiumLab的安装、主要功能…

2-119 基于matlab的合成孔径雷达(SAR)RDA(距离多普勒算法)、RMA(距离徙动算法)、CSA(线性调频变标算法)算法点目标成像与分析

基于matlab的合成孔径雷达(SAR)RDA(距离多普勒算法)、RMA(距离徙动算法)、CSA(线性调频变标算法)算法点目标成像与分析&#xff0c;RDA算法通过参考目标的多普勒历程完成对应匹配滤波器设计&#xff0c;获得同距离处不同目标相对于参考目标的方位位置。RMA是一种高分辨率的频域…

阿里测试之道-测试团队的发展之路

一、测试团队的发展之路 1.1、测试团队面临的困局 测试团队面临着不少的苦难&#xff0c;其中具有普遍性的有&#xff1a; 测试自动化难&#xff1a; 自动化测试程序编写成本高、执行时间长、维护成本高。测试的”技术债“在业务高速发展的过程中越积越多&#xff1a;一边修复…

ubuntu-24.04.1 系统安装

使用VMware虚拟机上进行实现 官网下载地址&#xff1a; https://cn.ubuntu.com/download https://releases.ubuntu.com 操作系统手册&#xff1a; https://ubuntu.com/server/docs/ &#xff08;里面包含安装文档&#xff09; 安装指南&#xff08;详细&#xff09;&#xff1a…