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

2024-08-30 01:28
文章标签 搜索算法 递归 记忆

本文主要是介绍【递归深搜之记忆化搜索算法】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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)); // 全部都初始化为-1return 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 和 nvis[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.chinasem.cn/article/1119405

相关文章

hdu 4517 floyd+记忆化搜索

题意: 有n(100)个景点,m(1000)条路,时间限制为t(300),起点s,终点e。 访问每个景点需要时间cost_i,每个景点的访问价值为value_i。 点与点之间行走需要花费的时间为g[ i ] [ j ] 。注意点间可能有多条边。 走到一个点时可以选择访问或者不访问,并且当前点的访问价值应该严格大于前一个访问的点。 现在求,从起点出发,到达终点,在时间限制内,能得到的最大

PHP实现二叉树遍历(非递归方式,栈模拟实现)

二叉树定义是这样的:一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,根据节点的访问位置不同有三种遍历方式: ① NLR:前序遍历(PreorderTraversal亦称(先序遍历)) ——访问结点的操作发生在遍历其左右子树之前。 ② LNR:中序遍历(InorderTraversal) ——访问结点的操作发生在遍历其左右子树之中(间)。 ③ LRN:后序遍历(PostorderT

oracle11.2g递归查询(树形结构查询)

转自: 一 二 简单语法介绍 一、树型表结构:节点ID 上级ID 节点名称二、公式: select 节点ID,节点名称,levelfrom 表connect by prior 节点ID=上级节点IDstart with 上级节点ID=节点值 oracle官网解说 开发人员:SQL 递归: 在 Oracle Database 11g 第 2 版中查询层次结构数据的快速

JS中【记忆函数】内容详解与应用

在 JavaScript 中,记忆函数(Memoization)是一种优化技术,旨在通过存储函数的调用结果,避免重复计算以提高性能。它非常适用于纯函数(同样的输入总是产生同样的输出),特别是在需要大量重复计算的场景中。为了彻底理解 JavaScript 中的记忆函数,本文将从其原理、实现方式、应用场景及优化方法等多个方面详细讨论。 一、记忆函数的基本原理 记忆化是一种缓存策略,主要用于函数式编

记忆化搜索【下】

375. 猜数字大小II 题目分析 题目链接:375. 猜数字大小 II - 力扣(LeetCode) 题目比较长,大致意思就是给一个数,比如说10,定的数字是7,让我们在[1, 10]这个区间猜。 如果猜大或猜小都会说明是大了还是小了,此外,我们还需要支付猜错数字对应的现金。 现在就是让我们定制一个猜测策略,确保准备最少的钱能猜对 如果采用二分查找,只能确保最小次数,题目要求的

Leetcode面试经典150题-128.最长连续序列-递归版本另解

之前写过一篇这个题的,但是可能代码比较复杂,这回来个简洁版的,这个是递归版本 可以看看之前的版本,两个版本面试用哪个都保过 解法都在代码里,不懂就留言或者私信 class Solution {/**对于之前的解法,我现在提供一共更优的解,但是这种可能会比较难懂一些(思想方面)代码其实是很简洁的,总体思想如下:不需要排序直接把所有数放入map,map的key是当前数字,value是当前数开始的

自然语言处理系列六十三》神经网络算法》LSTM长短期记忆神经网络算法

注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列六十三神经网络算法》LSTM长短期记忆神经网络算法Seq2Seq端到端神经网络算法 总结 自然语言处理系列六十三 神经网络算法》LSTM长短期记忆神经网络算法 长短期记忆网络(LSTM,Long S

【UVA】10651-Pebble Solitaire(直接递归或者记忆化)

不知道这个题UVA的数据是怎么的,用2个方法交了,第一次直接递归,第二次记忆化剪枝,时间竟然一样!? 直接郁闷了,简单的二进制表示状态和二进制运算。 14145176 10651 Pebble Solitaire Accepted C++ 0.009 2014-09-04 09:18:21 #include<cstdio>#include<algorithm>#inclu

笔试强训,[NOIP2002普及组]过河卒牛客.游游的水果大礼包牛客.买卖股票的最好时机(二)二叉树非递归前序遍历

目录 [NOIP2002普及组]过河卒 牛客.游游的水果大礼包 牛客.买卖股票的最好时机(二) 二叉树非递归前序遍历 [NOIP2002普及组]过河卒 题里面给的提示很有用,那个马的关系,后面就注意,dp需要作为long的类型。 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息publ

HCIA--实验十:路由的递归特性

递归路由的理解 一、实验内容 1.需求/要求: 使用4台路由器,在AR1和AR4上分别配置一个LOOPBACK接口,根据路由的递归特性,写一系列的静态路由实现让1.1.1.1和4.4.4.4的双向通信。 二、实验过程 1.拓扑图: 2.步骤: (下列命令行可以直接复制在ensp) 1.如拓扑图所示,配置各路由器的基本信息: 各接口的ip地址及子网掩码,给AR1和AR4分别配置