记忆化搜索

这篇具有很好参考价值的文章主要介绍了记忆化搜索。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、前言

二、简要谈谈记忆化搜索

三、最长滑雪道

1、题目

2、基本思路

3、python代码

四、立方体IV

1、上链接

2、基本思路

3、C++代码

4、python代码

5、发现的C++与python之间的输入区别

五、食物链

1、上链接

2、基本思路

3、C++代码(7/10)

4、python代码(9/10)


一、前言

对于学计算机的同学来说,学习算法是一件非常重要的事情,废话不多讲,我们来讲讲“记忆化搜索问题”。

二、简要谈谈记忆化搜索

什么是记忆化搜索呢?搜索的低效在于没有能够很好地处理重叠子问题;动态规划虽然比较好地处理了重叠子问题,但是在有些拓扑关系比较复杂的题目面前,又显得无奈。记忆化搜索正是在这样的情况下产生的,它采用搜索的形式和动态规划中递推的思想将这两种方法有机地综合在一起,扬长避短,简单实用,在信息学中有着重要的作用。

用一个公式简单地说:记忆化搜索=搜索的形式+动态规划的思想

记忆化搜索是类似于动态规划的,不同的是,它是倒做的“递归式动态规划”。如斐波那契数列、滑雪问题等等。

三、最长滑雪道

1、题目

记忆化搜索

2、基本思路

解决一道题前,首先考虑用什么算法。

对于这道题而言,最直接朴素的想法应该是对于每个点进行深搜或者广搜,最后枚举一遍答案但是注意,对于每个点而言,只要周围的点比他小,都可以被走到,因此这样做的话时间复杂度是指数级别的,AC不掉。

那么我们考虑优化。

首先,搜索的思路是没有错的,那么怎样优化它呢?我们观察到一个条件,那就是,某个点只能从数值更大的点转移过来,也就是说,整个答案序列应该是递减的 => 搜索时不可能出现循环 => 搜索顺序是具有拓扑结构的,即,在一个固定答案序列中,数值较小的点一定是在数值较大的点后被搜索到,那么我们就可以使用记忆化搜索了。(注意上述所说:具有拓扑序是使用记忆化搜索的必备条件)

我们用一个数组储存从每个点出发可以达到的最大长度,之后,若是从其他数值较大的点转移到这个点时,不必再往后搜索了,因为结果已经记录在数组中了,直接返回就好

3、python代码

n,m=map(int,input().split())
Map=[]
ans=0
for i in range(n):
    Map.append(list(map(int,input().split())))

dp=[[-1 for i in range(m)] for j in range(n)] # 先列再行

def dfs(x,y):
    global dp
    if dp[x][y]!=-1:
        return dp[x][y]
    
    t=1
    for i,j in [(1,0),(0,1),(-1,0),(0,-1)]:
        px=x+i
        py=y+j
        if 0<=px<n and 0<=py<m and Map[px][py]<Map[x][y]:
            t=max(t,dfs(px,py)+1)
    
    dp[x][y]=t
    return t

for i in range(n):
    for j in range(m):
        dfs(i,j)
    
for i in range(n):
    for j in range(m):
        ans=max(ans,dp[i][j])

print(ans)

四、立方体IV

1、上链接

691. 立方体IV - AcWing题库

2、基本思路

记忆化搜索

分析过程完全相同,只是转移条件发生了小小的变化,下一个点必须比上一个点大1。注意一下输出格式即可。

3、C++代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int n;
int g[N][N],f[N][N];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};

int dp(int x,int y){
	int& v=f[x][y];
	if(v!=-1) //f[x][y]不是-1说明它不是起点,其他点到当前点f[x][y],说明其他点的路径更长 
		return v;
	
	v=1;//四面都走不了说明我留在原地也是1 
	for(int i=0;i<4;i++){
		int a=x+dx[i],b=y+dy[i];
		if(a>=0&&a<n&&b>=0&&b<n&&g[a][b]==g[x][y]+1)
			v=max(v,dp(a,b)+1);
	}
	return v;
}

int main(){
	int T;
	cin>>T;
	for(int cases=1;cases<=T;++cases){
		cin>>n;
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				cin>>g[i][j];
			}
		}
		memset(f,-1,sizeof f);
		
		int id,cnt=0;
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				int t=dp(i,j);
				if(t>cnt || t==cnt && id>g[i][j]){ // &&比||的优先级高
					id=g[i][j];
					cnt=t;
				}
			}
		}
		printf("Case #%d: %d %d\n",cases,id,cnt);
	}
}

4、python代码

import copy

n=0
N=1100
g=[]
f=[[-1]*N for _ in range(N)]
_f=[[-1]*N for _ in range(N)]

def dp(x:int,y:int)->int:
    v=f[x][y]
    if v!=-1:
        return v;

    v=1
    for i,j in {(-1,0),(0,1),(1,0),(0,-1)}:
        a,b=x+i,y+j
        if 0<=a<n and 0<=b<n and g[a][b]==g[x][y]+1:
            v=max(v,dp(a,b)+1)

    f[x][y]=v
    return v


T=int(input())
for case in range(1,T+1):
    g.clear()    # 这里有个坑,要对列表进行清空
    n=int(input())  # 一旦回车便执行完毕
    for i in range(n):
        line=list(map(int,input().split()))
        g.append(line)

    f=copy.deepcopy(_f)

    _id,maxn=0,0

    for i in range(n):
        for j in range(n):
            t=dp(i,j)
            if t>maxn or t==maxn and _id>g[i][j]:
                _id=g[i][j]
                maxn=t

    print("Case #{}: {} {}".format(case,_id,maxn))
    

python代码是过不了的,原因在于输入的问题,下面有谈到这个问题。

5、发现的C++与python之间的输入区别

代码:

记忆化搜索

 终端输入输出:

记忆化搜索

我们能很清楚看到,cin一定要读取到内容,这样回车才能结束输入;

但是python的输入一旦回车马上结束输入,它会认为你输入了一个空字符。

五、食物链

1、上链接

2716. 食物链 - AcWing题库

2、基本思路

首先我们浏览题目 能得到一个很重要的线索:

这是一张拓扑图 即点与点之间存在拓扑关系,所以我们很自然的能想到使用记忆化搜素。

大体使用什么算法已经定下来了,接下来应该扣细节了。

我们可以想一下,怎样才能算作一条食物链,是不是一条从起点能走向终点的路径?

所以我们可以维护一个dp数组,数组储存从这个点出发(不一定是起点),有几条路径能走到终点。

那么起点和终点怎么判断呢,这里就需要用到拓扑排序中的术语了:起点(入度为0)、

终点(出度为0)

确定起点和终点之后,就需要思考这个搜索函数dfs该怎么写了。

首先我们可以先思考终点前的一个点,这个点的 dp 数组值是不是就应该是它连接着几个终点?比如A、B都是终点,然后 点C->A、C->B 有这两条路径,那么dp[C]=2 。

这样以此类推, 能通向 C 的点 D 就加上 dp[C] 的值和从点 D 能到达的点的 dp 值,如此往复递归直到起点。

需要注意的是,如果一个点已经被遍历过了,那么它的 dp 值就不会再被改变了,因为它的 dp 值只能被该点的下一个点更改。由于拓扑序的关系,该点之后的点不可能再被遍历到,因此该点的 dp 值不会再被改变。

3、C++代码(7/10)

#include<iostream>
#include<queue>
#include<vector>
using namespace std;

const int N=100009;
int n,m;
int in[N],out[N],chain[N];
vector<vector<int>> f(N,vector<int>(0,0));

int toposort(int x){
	int s=0;
	queue<int> q;
	q.push(x);
	while(!q.empty()){
		int now=q.front();
		q.pop();
		int len=f[now].size();
//		if(len==0&&in[now]!=0){
//			
//		}
		for(int i=0;i<len;i++){
			if(out[f[now][i]]==0)
				s+=1;
			else
				q.push(f[now][i]);
		}
	}
	return s;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int a,b;
		cin>>a>>b;
		out[a]++;
		in[b]++;
		f[a].push_back(b);
	}
	
	int sum=0;
	for(int i=1;i<=n;++i){
		if(in[i]==0){
			sum+=toposort(i);
		}
	}
	cout<<sum<<endl;
	return 0;
} 

4、python代码(9/10)

import sys
sys.setrecursionlimit(1000000) # 修改递归深度 其他语言可以忽略
 
n,m = map(int,input().split())
 
Map = [dict() for i in range(n+1)]   # 用字典来减少空间!!!
din = [0 for i in range(n+1)] # 每个点的入度
dout = [0 for i in range(n+1)] # 每个点的出度
 
for i in range(m) :
    a,b = map(int,input().split())
    din[b] += 1 ; dout[a] += 1
    Map[a][b] = 1
    
ST = [] ; EN = [] # 起点 终点
    
for i in range(1,n+1) :
    if din[i] == 0 : ST.append(i)
    elif dout[i] == 0 : EN.append(i)
    
dp = [-1 for i in range(n+1)] # 初始化为-1
 
def dfs(node) :
    global dp
    if dp[node] != -1 : # 如果被遍历过直接返回
        return dp[node]
    if node in EN : return 1 # 如果是终点返回 1
    dp[node] = 0
    for next in Map[node].keys() : # 否则累加该点能到的所有点的dp值
        dp[node] += dfs(next)
        
    return dp[node] # 返回
 
ans = 0    
for i in ST : dfs(i) ; ans += dp[i] # 从每个起点开始走 累加答案
print(ans)
    

注意领会其中思想即可,至于没完全AC我也是调了很久没办法(先挖个坑,迟点有空修正)。

以上,记忆化搜索

祝好

 文章来源地址https://www.toymoban.com/news/detail-498363.html

到了这里,关于记忆化搜索的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • DFS:记忆化搜索

    . - 力扣(LeetCode)

    2024年04月09日
    浏览(71)
  • 记忆化搜索

    目录 一、前言 二、简要谈谈记忆化搜索 三、最长滑雪道 1、题目 2、基本思路 3、python代码 四、立方体IV 1、上链接 2、基本思路 3、C++代码 4、python代码 5、发现的C++与python之间的输入区别 五、食物链 1、上链接 2、基本思路 3、C++代码(7/10) 4、python代码(9/10) 对于学计算机

    2024年02月10日
    浏览(26)
  • 【算法专题】记忆化搜索

    什么是记忆化搜索呢?记忆化搜索其实就是带了\\\"备忘录\\\"的递归,给递归加上一个\\\"备忘录\\\",递归每次返回的时候,将结果放到\\\"备忘录\\\"里面,在每次进入递归的时候,往\\\"备忘录\\\"里面看看,当前需要递归的数据时候在\\\"备忘录\\\"里存在,如果存在,那么就可以直接取此次的结果,

    2024年02月03日
    浏览(42)
  • 算法专题:记忆化搜索

    参考练习习题总集 没有什么特别知识,只有一些做题经验。要做这类型的题目,首先写出暴力搜索,然后写出记忆搜索,大概就是这个流程。感觉说了一些废话。 TLE:(自己写的难蚌代码) TLE:(一个较合适的思路) AC:(刚上手就放弃的屑) temp[i][j][k]:从s1[i]开始k个字符,从s

    2024年02月19日
    浏览(30)
  • 「学习笔记」记忆化搜索

    由于我一直对搜索情有独钟,因此,如果能写记忆化搜索的绝不会写 for 循环 DP。 文章部分内容来自 (texttt{OI-Wiki}) 记忆化搜索是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。 因为记忆化搜索确保了每个状态只访问一次,它也是一

    2024年02月08日
    浏览(39)
  • 掷骰子(从暴力搜索 到 记忆化搜索 到 动态规划)

    LeetCode上的 掷骰子模拟 题目描述通常如下: 题目:掷骰子模拟(Simulation of Dice Rolling) 原题链接 描述: 有一个骰子模拟器,每次投掷时都会生成一个1到6的随机数。不过,在使用这个模拟器时有一个约束条件:连续掷出数字i的次数不能超过rollMax[i](其中i从1开始编号)。

    2024年03月19日
    浏览(63)
  • 【虹科干货】谈谈Redis Enterprise实时搜索的过人之处

    我们都知道,用户在使用应用程序时候,对于速度有着越来越高的要求,真可谓是 “一秒也等不及”。而开发团队又该怎样来满足这种对于实时性的期望呢?   文章速览:   Redis Enterprise 实时搜索的应用场景 利用索引为开发人员带来更好的体验 Redis Enterprise 实时搜索的优势

    2024年02月08日
    浏览(47)
  • DFS(基础,回溯,剪枝,记忆化)搜索

    DFS(深度优先搜索) 基于递归求解问题,而针对搜索的过程 对于问题的介入状态叫初始状态,要求的状态叫目标状态 这里的搜索就是对实时产生的状态进行分析检测,直到得到一个目标状态或符合要求的最佳状态为止。对于实时产生新的状态的过程叫扩展 搜索的要点: 1.选定初

    2024年04月12日
    浏览(37)
  • 周赛379(排序、分类讨论、记忆化搜索(动态规划))

    简单 给你一个下标从 0 开始的二维整数数组 dimensions 。 对于所有下标 i ( 0 = i dimensions.length ), dimensions[i][0] 表示矩形 i 的长度,而 dimensions[i][1] 表示矩形 i 的宽度。 返回对角线最 长 的矩形的 面积 。如果存在多个对角线长度相同的矩形,返回面积最 大 的矩形的面积。

    2024年01月19日
    浏览(43)
  • 跳跃游戏 (DFS->记忆化搜索->动态规划/贪心证明)

            跳跃游戏是一种典型的算法题目,经常是给定一数组arr,从数组的某一位置i出发,根据一定的跳跃规则,比如从i位置能跳arr[i]步,或者小于arr[i]步,或者固定步数,直到到达某一位置,可能是数组的最后一个位置,也有可能是某一特别的数值处,也有可能在这个

    2024年02月03日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包