当前位置: 首页 > news >正文

【算法学习计划】回溯 -- 记忆化搜索

目录

前言

509.斐波那契数

62.不同路径

300.最长递增子序列

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

结语


今天我们来讲一讲回溯中一个典型的专题 -- 记忆化搜索

今天我们将用 4 道题目来带各位深度了解一下这个算法

(下文中的标题都是leedcode对应题目的链接)

前言

我们的记忆化搜索,是一种相当厉害的思维,但是并不是所有的dfs都能改成记忆化搜索,我们只有在分析问题的时候,发现能转化,我们才能转

但是如果能转化的话,代码写起来是非常简单的

举个栗子:
比如我们的斐波那契数列,假设我们现在要找的是第 7 个,那么要知道第 7 个,就要知道第 6 个和第 5 个,而要知道第 6 个,就要知道第 5 个和第 4 个

从这里开始,我们就会发现,5 的情况其实重复了,再往后遍历的话,4、3、2的情况全部都会重复计算,如果要找的不是第 7 个,而是第 10000 个,或者更多呢?包超时的

所有我们可以那一个数组 memo 记录一下这些节点的值

也就是当我们第一次将 5 这个位置算出来的时候,我们就将这个位置的值放进 memo 数组里面,当我们下次再遍历到这个位置的时候,直接在 memo 数组里面找即可

或者不是这个模型,是要我们找路径,如果我们当前这个路径能有一个值记录着,并且我们在递归的时候会多次访问这个地方的值的话,我们就可以用记忆化搜索

这些知识都会在我们下面的题目中有所体现

509.斐波那契数

这道题的题解在前言中被当作例子,代码如下:

class Solution {
public:
    int mem[32];

    int dfs(int n)
    {
        if(n <= 0) return 0;
        if(mem[n] != -1) return mem[n];
        mem[n] = dfs(n-1) + dfs(n-2);
        return mem[n];
    }

    int fib(int n) 
    {
        if(n < 2) return n;
        memset(mem, -1, sizeof mem);
        mem[0] = 0, mem[1] = 1;
        return dfs(n);
    }
};

62.不同路径

这一道题目如果正常递归的话,我们要去最右下角,那么我们就应该从最右下角进行递归,因为每一个位置都是从上或者从左来的

但是我们这里,做过的路径会出现相互重叠的情况,哪怕只是一部分,所以我们也可以使用记忆化搜索

至于递归的逻辑,就是上和左:

vis[m-1][n] = dfs(m-1, n);

vis[m][n-1] = dfs(m, n-1);

代码如下:

class Solution {
public:
    int vis[101][101] = {0};
    int dfs(int m, int n)
    {
        if(vis[m][n]) return vis[m][n];
        if(m == 0 || n == 0) return 0;

        vis[m-1][n] = dfs(m-1, n);
        vis[m][n-1] = dfs(m, n-1);
        return vis[m-1][n] + vis[m][n-1];
    }
    int uniquePaths(int m, int n) 
    {
        vis[1][1] = 1;

        return dfs(m, n);    
    }
};

300.最长递增子序列

这一题的话,我们如果纯dfs,那么就是固定一个位置,然后从这个位置开始,往后递归,只有比当前的数要大才能进入下一个递归逻辑,最后返回从这个位置开始的最长递增子序列的长度

但是我们在中途的时候,我们遇到的每一个位置都能算是一个新的起点,所以从这个起点开始出发的话,那么必然是会有一个最长的长度的

这时我们就可以开始往里面加记忆化搜索的逻辑了,我们每遍历到一个位置,就将从这儿开始的最长递增子序列的长度给放进数组memo里面,当我下次再遍历到这个位置的时候,我们就可以直接从那个数组里面找

代码如下:

class Solution {
public:
    int vis[2501] = {0};
    int dfs(vector<int>& nums, int pos)
    {
        if(vis[pos]) return vis[pos];
        int ret = 1;
        for(int i = pos + 1; i < nums.size(); i++)
        {
            if(nums[i] <= nums[pos]) continue;
            ret = max(ret, dfs(nums, i) + 1);
        }    
        vis[pos] = ret;
        return ret;
    }

    int lengthOfLIS(vector<int>& nums) 
    {
        int ret = 1;
        for(int i = 0; i < nums.size(); i++)
        {
            ret = max(ret, dfs(nums, i));
        }    
        return ret;
    }
};

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

其实,说难也不难,因为这道题目的本质就是,从每一个起点开始,不断往四面八方递归,只有严格大于才能进行下一步的递归操作,然后每一次把四面八方都遍历完了,我们就更新一下最长的值即可

但是由于我们每一个位置都能视为一个起点,那么我们从每一个起点开始的最长长度也是一个定值,所以我们就能够使用记忆化搜索的逻辑,也就是,创建一个数组,然后每遍历到一个位置就将这个结果放进数组里面

如果新遍历到一个位置的话,那么我们就看看数组里面有没有这个位置的值,有的话,直接返回这个位置的值就好了

代码如下:

class Solution {
public:
    int n, m;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    bool check[201][201];
    int vis[201][201];

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

    int longestIncreasingPath(vector<vector<int>>& matrix) 
    {
        memset(vis, 0, sizeof vis);
        int ret = 1;
        n = matrix.size(), m = matrix[0].size();
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
            {
                memset(check, false, sizeof(check));
                ret = max(ret, dfs(matrix, i, j));
            }
        return ret;
    }
};

结语

这篇文章到这里就结束啦!!~( ̄▽ ̄)~*

如果觉得对你有帮助的,可以多多关注一下喔

相关文章:

  • StringTemplate修仙指南:字符串处理的“言出法随“大法
  • 智能物联网网关策略部署
  • vue3+vite+js项目引入electron构建跨平台桌面应用
  • Excel 自动执行全局宏
  • 项目进度延误的十大原因及应对方案
  • 4-10记录(
  • 聊天室项目Day3之服务器的http的get和post回复实现
  • 软件信息安全性测试如何进行?有哪些注意事项?
  • 神经网络入门—自定义神经网络续集
  • 2. 单词个数统计
  • WPS JS宏编程教程(从基础到进阶)-- 第六部分:JS集合与映射在 WPS 的应用
  • 关于使用@Slf4j后引入log,idea标红解决办法
  • Linux | I.MX6ULL外设功能验证(11)
  • FreeRTOS项目工程完善指南:STM32F103C8T6系列
  • 【结合vue源码,分析vue2及vue3的数据绑定实现原理】
  • 【力扣hot100题】(083)零钱兑换
  • Redis 持久化机制详解:RDB/AOF 过程、优缺点及配置。Redis持久化中的Fork与Copy-on-Write技术解析。
  • android studio 2022打开了v1 签名但是生成的apk没有v1签名问题
  • C# 组件的使用方法
  • Python proteinflow 库介绍
  • 溧阳做网站的哪家好/境外电商有哪些平台
  • 泰州做房产的网站/海外推广营销平台
  • 北京的做网站公司/网络推广平台有哪些
  • 做网站要学编程麽/产品如何做市场推广
  • 云羽网络做网站怎么样/成人英语培训
  • 南京网站建设cnee/超级外链发布工具