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

【算法】记忆化搜索

记忆化搜索

  • 1.斐波那契数
  • 2.Function
  • 3.天下第一
  • 4.滑雪

记忆化搜索也是一种剪枝策略。通过一个 “备忘录”,记录第一次搜索到的结果,当下一次搜索到这个状态时,直接在 “备忘录” 里面找结果。记忆化搜索,有时也叫动态规划。

  1. 如何实现记忆化搜索?
  • 创建备忘录。
  • 递归的时候,先查看备忘录中是否存在结果。
  • 递归返回的时候,先将结果存到备忘录中。
  1. 所有的递归以及暴搜,都能用记忆化搜索来进行优化吗?只有在递归的过程中,出现了大量 “完全相同的问题” 时,才能用记忆化搜索优化。
  2. 注意:初始化备忘录时,一定不能存在递归过程中可能出现的值。

1.斐波那契数

斐波那契数

在这里插入图片描述

解法:记忆化搜索

在搜索的过程中,如果发现特别多完全相同的子问题,就可以添加一个备忘录,将搜索的结果放在备忘录中。下一次在搜索到这个状态时,直接在备忘录里面拿值。

class Solution 
{
    int f[35]; //备忘录

public:
    int dfs(int n)
    {
        //搜索之前先查看备忘录中是否存在结果
        if(f[n] != -1) return f[n];

        if(n == 0 || n == 1) return n;

        //返回之前,将结果存放在备忘录中
        return f[n] = dfs(n - 1) + dfs(n - 2);
    }

    int fib(int n) 
    {
        //先初始化备忘录成一定不会存在的值
        memset(f, -1, sizeof(f));
        return dfs(n);
    }
};

2.Function

P1464 Function

在这里插入图片描述

解法:记忆化搜索

题目叙述的非常清楚,我们仅需按照「题目的要求」把「递归函数」写出来即可。但是,如果不做其余处理的话,结果会「超时」。因为我们递归的「深度」和「广度」都非常大。通过把「递归展开图」画出来,我们发现,在递归过程中会遇到大量「一模一样」的问题,如下图(因为递归展开过于庞大,这里只画出了一部分):

在这里插入图片描述

因此,可以在递归的过程中,把每次算出来的结果存在一张「备忘录」里面。等到下次递归进入「一模一样」的问题之后,就「不用傻乎乎的展开计算」,而是在「备忘录里面直接把结果拿出来」,起到大量剪枝的效果。

#include<iostream>
using namespace std;

typedef long long LL;

const int N = 25;

LL a, b, c;
LL f[N][N][N]; //备忘录

LL dfs(LL a, LL b, LL c)
{
    //先判断边界情况:防止备忘录数组f越界
    if(a <= 0 || b <= 0 || c <= 0) return 1;
    if(a > 20 || b > 20 || c > 20) return dfs(20, 20, 20);
    
    //再查看备忘录中是否存在结果
    if(f[a][b][c]) return f[a][b][c];
    
    if(a < b && b < c)
        return f[a][b][c] = dfs(a, b, c - 1) + dfs(a, b - 1, c - 1) - dfs(a, b - 1, c);
    else
        return f[a][b][c] = dfs(a - 1, b, c) + dfs(a - 1, b - 1, c) + dfs(a - 1, b, c - 1) - dfs(a - 1, b - 1, c - 1);
}

int main()
{
    while(cin >> a >> b >> c)
    {
        //多组测试数据:不需要清空
        if(a == -1 && b == -1 && c == -1) break;
        
        printf("w(%lld, %lld, %lld) = %lld\n", a, b, c, dfs(a, b, c));
    }
    
    return 0;
}

3.天下第一

P5635 【CSGRound1】天下第一

在这里插入图片描述

解法:记忆化搜索

用递归模拟整个游戏过程:dfs(x, y) 的结果可以由 dfs((x + y) % p, (x + y + y) % p) 得到。因为测试数据是多组的,并且模数都是 p,再加上递归的过程中会递归的相同的问题,所以可以把递归改写成记忆化搜索。其中:

  1. f[x][y] = 1,表示 cbw 赢。
  2. f[x][y] = 2,表示 zhouwc 赢。
  3. f[x][y] = 3,表示这个位置已经被访问过,如果没被修改成 1 或者 2,那就表明平局。

注意事项:这道题的数据范围很大,用 int 类型创建二维数组空间会溢出。但是我们的最终结果仅有三种情况,所以可以用 char 类型来存储最终结果,节省空间。

#include<iostream>
using namespace std;

const int N = 1e4 + 10;

int x, y, p;
char f[N][N]; //备忘录

char dfs(int x, int y)
{
    //查看备忘录中是否存在结果
    if(f[x][y]) return f[x][y];
    
    //没有结果时表示:这个状态已经访问过了, 之后再遇到时, 表示平局 
    f[x][y] = '3';
    
    //修改为正确的结果再返回, 否则该位置是'3'
    if(x == 0) return f[x][y] = '1'; 
    if(y == 0) return f[x][y] = '2';
    
    //同理: 修改为正确的结果再返回
    return f[x][y] = dfs((x + y) % p, (x + y + y) % p);
}

int main()
{
    int T; cin >> T >> p;
    while(T--)
    {
        cin >> x >> y;
        char ret = dfs(x, y);
        if(ret == '1') cout << 1 << endl;
        else if(ret == '2') cout << 2 << endl;
        else cout << "error" << endl;
    }
    
    return 0;
}

4.滑雪

P1434 [SHOI2002] 滑雪

在这里插入图片描述

解法:记忆化搜索

暴力枚举:遍历整个矩阵,看看以当前位置为起点,最远能滑行多远的距离。在所有情况里面,取最大值即可。

如何求出以 [i, j] 为起点的最大距离?

  1. 从 [i, j] 位置上下左右瞅一瞅,如果能滑过去,就看看以下一个位置为起点,最远能滑行多远的距离。
  2. 找出四个方向上的最远距离,然后 +1。

因为出现相同子问题,所以可以用 dfs 来解决。又因为在搜索的过程中会遇到一模一样的问题,因此可以把递归改成记忆化搜索的方式。

#include<iostream>
using namespace std;

const int N = 110;

int n, m;
int a[N][N];
int f[N][N]; //备忘录

//上下左右四个方向
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};

int dfs(int i, int j)
{
    if(f[i][j]) return f[i][j]; //先查看备忘录中是否存在结果
    
    int len = 1;
    for(int k = 0; k < 4; k++)
    {
        int x = i + dx[k], y = j + dy[k];
        if(x >= 1 && x <= n && y >= 1 && y <= m && a[i][j] > a[x][y])
        {
            len = max(dfs(x, y) + 1, len);
        }
    }
    return f[i][j] = len; //先往备忘录中存,再返回最大滑坡
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            cin >> a[i][j];
        }
    }
    
    int ret = 1;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            ret = max(ret, dfs(i, j));
        }
    }
    cout << ret << endl;
    
    return 0;
}

http://www.kler.cn/a/523208.html

相关文章:

  • UE求职Demo开发日志#15 思路与任务梳理、找需要的资源
  • 新增文章功能
  • Qt文件操作
  • 模糊综合评价
  • java多线程学习笔记
  • 知识库建设对提升团队协作与创新能力的影响分析
  • RoboVLM——通用机器人策略的VLA设计哲学:如何选择骨干网络、如何构建VLA架构、何时添加跨本体数据
  • 网站结构优化:加速搜索引擎收录的关键
  • 【AI论文】扩散对抗后训练用于一步视频生成总结
  • 菜鸟之路Day10一一集合进阶(三)
  • 网络工程师 (6)操作系统概述
  • 浅析 CSS 中块级框,块容器框和块框
  • 2024年记 | 凛冬将至
  • 【Java-数据结构】Java 链表面试题下 “最后一公里”:解决复杂链表问题的致胜法宝
  • 快速分析LabVIEW主要特征进行判断
  • Java面试题2025-并发编程基础(多线程、锁、阻塞队列)
  • Java基于SSM框架的互助学习平台小程序【附源码、文档】
  • GPS信号捕获【时间-频率空间搜索方法】
  • 指定dpkg安装deb包时的安装路径
  • SpringBoot 使用海康 SDK 和 flv.js 显示监控画面
  • objection的简单使用
  • 一图展示汽车和航空电子领域的安全和互操作性解决方案的概览
  • https数字签名手动验签
  • PythonFlask框架
  • Effective Objective-C 2.0 读书笔记—— objc_msgSend
  • 跨平台物联网漏洞挖掘算法评估框架设计与实现文献综述:物联网设备漏洞挖掘的挑战和机遇