【递归深搜之记忆化搜索算法】

news2024/11/25 22:42:37

1. 斐波那契数

解法一:递归

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);
    }
};

解法二:记忆化搜索

class Solution {
    int nums[31]; // 备忘录
public:
    int fib(int n) {
        memset(nums, -1, sizeof(nums)); // 全部都初始化为-1
        return dfs(n);
    }

    int dfs(int n)
    {
        // 在备忘录里面查询一下
        if(nums[n] != -1)
        {
            // 如果已经计算过直接返回
            return nums[n];
        }

        // 否则在返回的时候添加到备忘录
        if(n == 0 || n == 1)
        {
            nums[n] = n;
            return nums[n];
        }
        else
        {
            nums[n] = dfs(n - 1) + dfs(n - 2);
            return nums[n];
        } 
    }
};  

解法三:动态规划

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. 不同路径 

这个题目我们首先想到就是递归深搜右和下两个方向的路径,直到达到终点,此时我们统计次数,但是别忘记了要恢复现场哟

class Solution {
    int dx[2] = {0, 1};
    int dy[2] = {1, 0};
    bool vis[101][101] = { false };
    int ret;

public:
    int uniquePaths(int m, int n) {
        dfs(0, 0, m, n);
        return ret;
    }

    void dfs(int i, int j, int m, int n)
    {
        vis[i][j] = true;
        if(i == n - 1 && j == m - 1) // 注意这里 j 和 m 的位置
        {
            ret++;
            vis[i][j] = false; // 回溯:恢复访问状态
            return;
        }
        for(int k = 0 ; k < 2; k++)
        {
            int x = dx[k] + i;
            int y = dy[k] + j;
            if(x >= 0 && x < n && y >=0 && y < m && !vis[x][y]) // 注意这里 x 和 y 的范围检查
            {
                dfs(x, y, m, n); // 传递正确的 m 和 n
                vis[x][y] = false; // 回溯:恢复访问状态
            }
        }
    }
};

但是我们点击运行发现程序报错了,超时了,因为我们题目存在大量的重复的递归,所以我们这个题目需要采用记忆化手搜索去解决。

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

    int dfs(int i, int j)
    {
        if(i == 0 || j == 0) return 0;
        if(i == j && j == 1) return 1;
        return dfs(i - 1, j) + dfs(i, j - 1);
    }
};

class Solution {
public:
    int uniquePaths(int m, int n) {
        // 添加备忘录
        vector<vector<int>> nums(m + 1, vector<int>(n + 1));
        return dfs(m, n, nums);
    }

    int dfs(int i, int j, vector<vector<int>>& nums)
    {   
        // 查找备忘录
        if(nums[i][j])
        {
            return nums[i][j];
        }

        if(i == 0 || j == 0)
        {
            // 因为数组原本内容就是0,这里就不用填写到备忘录
            return 0;
        }
        if(i == j && j == 1)
        {
            nums[i][j] = 1;
            return nums[i][j];
        } 

        nums[i][j] = dfs(i - 1, j, nums) + dfs(i, j - 1, nums);
        return nums[i][j];
    }
};

同时我们这里还可以直接修改成动态规划的形式

class Solution {
public:
    int uniquePaths(int m, int n) {
        // 添加dp表
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        // 因为数组原本内容就是0,这里就不用填写到dp表中
        dp[1][1] = 1;
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++)
                { 
                    if(i == 1 && j == 1)
                        continue;
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                }
        return dp[m][n];
    }
};

3. 最长递增子序列

我们看到这个题目,依然是先画出我们的决策树,先来看看决策树什么样子

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int ret = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            // 考虑以所有位置为起点的情况
            ret = max(ret, dfs(nums, i));
        }
        return ret; 
    }
    int dfs(vector<int>& nums, int pos)
    {
        int ret = 1; // 起始位置算一个子序列
        for(int i = pos + 1; i < nums.size(); i++)
        {
            if(nums[pos] < nums[i])
            {
                ret = max(ret, dfs(nums, i) + 1);
            }
        }
        return ret;
    }
};

但是此时会超时,我们依然要使用记忆化搜索去解决。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> mem(nums.size());
        int ret = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            // 考虑以所有位置为起点的情况
            ret = max(ret, dfs(nums, i, mem));
        }
        return ret; 
    }
    
    int dfs(vector<int>& nums, int pos, vector<int>& mem)
    {
        // 查找备忘录
        if(mem[pos])
            return mem[pos];
        int ret = 1; // 起始位置算一个子序列
        for(int i = pos + 1; i < nums.size(); i++)
        {
            if(nums[pos] < nums[i])
            {
                // 递归下一层,并且记录上当层子序列
                ret = max(ret, dfs(nums, i, mem) + 1);
            }
        }
        // 添加到备忘录
        mem[pos] = ret;
        return mem[pos];
    }
};

此时我们也可以改成动态规划的代码

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        // dp(i) 表示以i位置为起点的最长递增子序列的个数
        // 填表顺序 -> 从后往前
        vector<int> dp(nums.size(), 1);
        int ret = 0;
        for(int i = nums.size() - 1; i >= 0; i--)
        {
            for(int j = i + 1; j < nums.size(); j++)
            {
                if(nums[j] > nums[i])
                {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            ret = max(dp[i], ret);
        }
        return ret; 
    }
};

4. 猜数字大小II

class Solution {
    int mem[201][201];
public:
    int getMoneyAmount(int n) {
        return dfs(1, n);
    }

    int dfs(int left, int right)
    {
        if(left > right)
        {
            return 0;
        }
        if(left == right)
        {
            return 0;
        }
        if(mem[left][right] != 0)
            return mem[left][right];
        int ret = INT_MAX;
        for(int i = left; i <= right; i++) // 随机选择一个值
        {
            int l = dfs(left, i - 1);
            int r = dfs(i + 1, right);
            ret = min(ret, i + max(l,r));
        }
        mem[left][right] = ret;
        return mem[left][right];
    }
};

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

class Solution {
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int m, n;
    int nums[201][201];
public:
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        m = matrix.size();
        n = matrix[0].size();
        int ret = 0;
        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(nums[i][j] != 0)
            return nums[i][j];
        int step = 1;
        for(int k = 0; k < 4; k++)
        {
            int x = i + dx[k];
            int y = j + dy[k];
            if(x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j])
            {
                step = max(step, dfs(matrix, x, y) + 1);
            }
        }
        nums[i][j] = step;
        return step;
    }
};

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

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

相关文章

carbonyl浏览器使用

仓库 carbonyl上提供了两种在线方式安装&#xff0c;一是docker方式 $ docker run --rm -ti fathyb/carbonyl https://youtube.com另一种是 $ npm install --global carbonyl $ carbonyl https://github.com此外还提供了mac和linux的二进制包&#xff0c;可直接下载运行。 二…

[BFS广度优先搜索] 迷宫

描述 给定一个仅由数字 0 与 1 组成的 n 行 m 列的迷宫。若你位于一格 0 上&#xff0c;那么你可以移动到相邻 4 格中的任意一格 1 上&#xff1b;同样地&#xff0c;若你位于一格 1 上, 那么你可以移动到相邻 4 格中的任意一格 0 上。 现在&#xff0c;有 q 次询问。每次询问…

11.STL

STL阶段 禁止复制 文本查询扩展作业解析 get_file函数的作用就是进行预处理操作&#xff0c;将文件中的每一行的内容放在shared_ptr<vector<string>> file里面进行存储&#xff1b;然后对每一个单词进行处理&#xff0c;将单词与行号放在map<string, shared_p…

【C/C++】C++类与对象基本概念(抽象封装、类的定义与使用、构造函数、析构函数、静态成员、友元)

目录 七、类与对象基本概念7.1 抽象7.2 类的定义与声明7.3 访问控制7.4 类的实现与使用7.5 对象指针、this指针与对象引用7.6 构造函数7.7 析构函数7.8 拷贝构造函数7.9 类类型作为函数参数7.10 对象数组7.11 静态成员7.12 常对象与常成员&#xff08;const&#xff09;7.13 友…

金融知识普及月答题活动

金融知识普及月答题活动 关键词&#xff1a;金融安全、风险防范、金融常识、反诈宣传 推荐功能&#xff1a;答题、倡议书 宣传角度&#xff1a; 1. 普及金融知识&#xff1a;讲解货币、信用、利率、汇率等基本金融概念&#xff0c;以及储蓄、贷款、信用卡、保险等常见金融产…

揭秘Xinstall:如何降低你的App推广成本?

在移动互联网时代&#xff0c;App推广成为了每个开发者都必须面对的问题。然而&#xff0c;随着市场竞争的加剧&#xff0c;App推广成本也水涨船高&#xff0c;让许多开发者望而却步。今天&#xff0c;我们就来聊聊如何借助Xinstall这一神器&#xff0c;有效降低App推广成本&am…

电商人必看:1个工具,5倍效率,批量处理图片就是这么简单

作为电商运营者或经常处理图片的你&#xff0c;是否厌倦了繁琐的图片编辑工作&#xff1f;今天&#xff0c;我要分享一个实用的解决方案——图片批量处理工具。 神器介绍&#x1f447; 千鹿设计助手&#xff0c;是一款轻量级、功能非常丰富的设计插件工具合集软件。 拥有多款…

宏集MIRO-L230工业路由器: 一站式全球联网解决方案

在日益互联的世界中&#xff0c;全球覆盖的稳定连接已成为业务成功的关键因素。宏集非常高兴地向您介绍我们的最新创新产品——MIRO-L230工业路由器&#xff0c;这是一款为现代企业量身定制的LTE路由器&#xff0c;为用户带来前所未有的稳定连接体验。 宏集MIRO-L230工业路由器…

Leetcode3234. 统计 1 显著的字符串的数量

Every day a Leetcode 题目来源&#xff1a;3234. 统计 1 显著的字符串的数量 解法1&#xff1a;枚举左端点 注意到&#xff0c;如果子串中的 0 非常多&#xff0c;多到 0 的个数的平方比 1 的个数都要大&#xff0c;那么这样的子串必然不是 1 显著子串。 设 cnt0 为子串中…

使用excel把json文件转为表格

json文件格式 [ { "ID": "16", "名称": "测站", "管理ID": "3", "管理名称": "土", "辅助信息": { "百度经度&qu…

macos MacPort 包管理工具安装和使用

在macos v10.15版本中, xz, python等软件无法使用brew安装, 原因是brew对于旧版本的macos不再支持, 但是我们可以使用另外一个macos下的包管理工具来安装brew无法安装的软件, macport 是一个和brew类似的macos下的一个非常优秀的软件包安装管理工具. MacPort安装前提条件 安…

解锁高效API测试之道:Apipost安装

在当今这个以API为中心的数字时代&#xff0c;无论是开发、测试还是管理API&#xff0c;一个得心应手的工具都能显著提升工作效率。如果你正寻求一款既强大又易于使用的API测试工具&#xff0c;那么Apipost绝对是你不容错过的选择。 接下来&#xff0c;让我们一起去完成这款软件…

zsh 添加 用户@主机 路径显示

export PROMPT"%F{green}%n%F{blue}%F{yellow}%m%F{cyan}:[%~]%f %# " export PROMPT"%F{green}%n%F{blue}%F{yellow}%m%F{cyan}:[%~]%f %$ " bash则为 PS1\u\h:\w\$

vue3之vite配置vite-plugin-mock使用mock轻松创建模拟数据提高开发效率

文章目录 什么是Mock数据使用Mock数据的优点Mock数据使用步骤一、安装依赖mockjs、vite-plugin-mock二、vite.config.ts 文件中配置三、在根目录下创建mock文件四、编写api接口调用文件1、src文件夹下新建utils/request.ts2、src文件夹下新建api/user.ts 五、业务页面调用六、M…

ESXI8虚拟机vmdk文件备份到本地硬盘

1. Esxi开启ssh服务 点击“管理”→“服务” →“TSM-SSH”&#xff0c;右击启动 2. 把datastore1中所有虚拟机文件(包括vmdk文件)复制到d:\E1 使用xshell等工具SSH登录ESXI8后台&#xff0c;确认datastore1的具体文件夹路径 在win10&#xff0c;使用cmd&#xff0c;在d:\E1…

docker续3:dockerfile

一、使用Dockerfile创建应用镜像 在Docker file中定义所需要执⾏的指令&#xff0c;使⽤ docker build创建镜像&#xff0c;过程中会按照dockerfile所定义的内容进⾏打开临时性容器&#xff0c;把docker file中命令全部执⾏完成&#xff0c;就得到了⼀个容器应⽤镜像&#xff…

力扣第二阶段Days34

1,题目描述-找出字符串中所有的字母异位词 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 2&#xff…

安科瑞预付费电能计量装置 功能介绍

安科瑞DTSY1352 三相无线预付费电能表是为测量额定频率 50Hz 的三相交流有功电能设计的高性能设备。这些电能表不仅支持预付费控制、负载和时间控制&#xff0c;恶性负载控制等多种控制模块&#xff0c;还具备多种通信功能&#xff0c;包括 RS485、NB、4G、LoRa、LoRaWAN、Wi-F…

SpringIoc体系结构设计

IOC容器的整体功能 从这个图仔细体会Spring框架加载Bean的过程. 有一个全局观. 1:加载Bean的配置&#xff08;比如xml配置 注解配置)不同类型资源的加载&#xff0c;解析成生成统一Bean的定义. 2:根据Bean的定义加载生成Bean的实例&#xff0c;并放置在Bean容器中 3:对容器中…

【C++ Qt day2】

2.完成课堂上的多文件编译 head.h main.cpp student.cpp 3. 自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h…