ACM 记忆化搜索

news2025/7/28 23:17:42

一.记忆化搜索概述

1.概念

        搜索是一种简单有效但是效率又很低下的算法结构,其低效的原因主要在于存在很多重叠子问题。而记忆化搜索则是在搜索的基础上,利用数组来记录已经计算出来的重叠子问题状态,进行合理化的剪枝,从而降低时间复杂度。这个记录状态的过程就是记忆化的过程,我们需要找到不同搜索层次之间的子问题、状态转移关系,这与动态规划的思想又不谋而合。

        简单来说,记忆化搜索是一种典型的空间换时间的思想,记忆化搜索 = 深度优先搜索实现 + 动态规划思想(记录状态、剪枝)。

2.图示

        此处以某个记忆化搜索题目的结构树为例。在搜索过程中,我们将问题的搜索树画出来如下所示。左侧为暴力搜索的搜索树,而右侧为记忆化搜索剪枝的搜索树。

         记忆化搜索可以看作是动态规划的前置过程,或者说记忆化搜索一般是自顶向下的,而动态规划一般是自底向上的。

二.例题

1.LeetCode 45. 跳跃游戏改

        给定一个长度为 n 的整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处(0 <= j <= nums[i])

        返回到达 nums[n - 1] 的最小跳跃次数。已知跳跃次数有上限K,若无法在K次内到达则返回 -1

(1)记忆化搜索 

        对于该题,我们可以先求出到达终点的最小跳跃次数 t,然后比较 t 与上限 K 的大小,若t > K则返回 -1 。

        按照搜索的思想来说,我们在到达某个索引 i 时,该位置到达终点的最小跳跃次数取决于 min(dfs(i) , dfs(i+j) + 1),0 <= j <= nums[i] ;按照记忆化的思想,像最短路一样,某个位置 i 到达终点的最小跳跃次数应该是确定的,属于重叠子问题,不应该重复搜索,因此可以使用数组记录每个位置的最小次数。

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000 + 7;
int n,k,nums[maxn],mem[maxn];

int dfs(int index){
    if(index >= n-1)return 0;
    if(mem[index]!=-1)return mem[index];//记忆化
    int ans = -1;
    for(int i = 1;i<=nums[index];i++){
        int res = dfs(index+i)+1;
        if(res != 0){
            ans = ans==-1?res:min(ans,res);
        }
    }
    return mem[index] = ans;
}

int main()
{
    memset(mem,-1,sizeof(mem));
    scanf("%d%d",&n,&k);
    for(int i = 0;i<n;i++){
        scanf("%d",&nums[i]);
    }
    dfs(0);
    if(mem[0] == -1 || mem[0] > k)printf("-1\n");
    else printf("%d\n",mem[0]);
}

(2)贪心

        记忆化搜索的方式可行,但是复杂度还是有点高会超时。我们重新来审视这个题,每个位置处的跳跃距离是固定的、相互独立的,与之前的子节点和之后的子节点都是没关系的,也就是说该题其实与动态规划思想无关。

        考虑现在跳到了位置 i ,那么接下来应该选择跳到 i+1 ~ i+nums[i] 的哪个位置呢?答案是「贪心」地选择能跳跃到距离最后一个位置最远的那个位置(即使得“探索序列”能够拓宽最远的那个位置),原因是以该位置为下一次起跳点所能到达的地方,由于其是最远的,所以能够覆盖其他所有的起跳点位置范围,这肯定是最优的。

        当一次 跳跃 结束时,从下一个格子开始,到现在 能跳到最远的距离 是下一次 跳跃 的 起跳点。所以跳完一次之后,不断更新维护下一次 起跳点的范围。在新的范围内跳,更新 能跳到最远的距离

int jump(int len){
    int ans = 0;
    int start = 0,last = 0; //初始起跳范围 [0,0] 闭区间
    while(last < len - 1){
        int maxpos = 0;
        //选择下一次跳哪个
        for(int i = start;i<=last;i++){
            maxpos = max(maxpos,i+nums[i]);
        }
        start = last+1; // 下一次起跳点范围开始的格子
        last = maxpos;  // 下一次起跳点范围结束的格子
        ans++; // 跳跃次数
    }
    return ans;
}

2. 青蛙过河

        一只青蛙想要过河。 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。 青蛙可以跳上石子,但是不可以跳入水中。

        假设河流中一共有 n 个石子,给你每个石子的河流单元格位置的列表 stones(用单元格序号升序表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一块石子上)。

        开始时, 青蛙默认已站在第一块石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格1 跳至单元格 2 )。如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1 个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。

输入:stones = [0,1,3,5,6,8,12,17]
输出:true
解释:青蛙可以成功过河,按照如下方案跳跃:跳 1 个单位到第 2 块石子, 然后跳 2 个单位到第 3 块石子, 接着 跳 2 个单位到第 4 块石子, 然后跳 3 个单位到第 6 块石子, 跳 4 个单位到第 7 块石子, 最后,跳 5 个单位到第 8 个石子(即最后一块石子)。

        该题题意与上一题的跳跃游戏类似,但是二者很大的一个不同就是每个位置的跳跃范围:跳跃游戏中每个位置的跳跃范围是固定的、相互独立的,而该题中每个位置的跳跃范围受到上一次跳跃的影响,具有子结构,因此该题优先考虑动态规划或记忆化搜索。 

        对于记忆化搜索来说,我们可以仍然去搜索所有可能的路径。但是对于每个节点来说,他的状态主要受到两个因素的影响:当前所在 stone 下标位置、上一次跳的步长,这两个因素决定了该位置后续的最优解。因此我们可以使用一个二维数组 dp[i][j] 保留位置 i 时,上个步长为 j 时的能否到达状态,以此进行记忆化搜索。

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000 + 7;
int n,stones[maxn];
vector<unordered_map<int, bool>> dp;

bool dfs(int index,int preSteps){
    if(index == n-1)return true;
    if(index >= n)return false;
    if(dp[index].count(preSteps))return dp[index][preSteps];
    bool res = false;
    for(int i = preSteps-1;i<=preSteps+1;i++){
        if(i<=0)continue;
        int nextpos = lower_bound(stones+index, stones+n, stones[index]+i) - stones;
        if(nextpos < n && stones[nextpos] == stones[index]+i){
             res = dfs(nextpos,i);
             if(res)break;
        }
    }
    return dp[index][preSteps] = res;
}

int main()
{
    scanf("%d",&n);
    dp.resize(n); // 初始化 vector 空间长度,int默认填充0,此处默认填充 空map
    for(int i = 0;i<n;i++){
        scanf("%d",&stones[i]);
    }
    bool ans = dfs(0,0);
    if(ans)printf("true\n");
    else printf("false\n");
    return 0;
}

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

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

相关文章

高防CDN的知识了解

高防CDN是一种基于CDN&#xff08;内容分发网络&#xff09;技术的网络安全服务&#xff0c;旨在提供高级的防御措施来保护网站或应用程序免受DDoS&#xff08;分布式拒绝服务&#xff09;攻击和其他网络安全威胁。CDN是一种通过将内容分发到全球多个节点来加速网站或应用程序的…

DBC 文件

概述Vector的DBC文件描述了CAN网络的通信规范&#xff0c;通过定义signal可以表示CAN帧中的各个物理信号的含义。通过CANdb Editor软件可以创建和修改DBC文件&#xff0c;一般监控或控制CAN网络内的节点&#xff0c;不需要解析DBC文件里的全部信息&#xff0c;因为有些信息是给…

前端借助Canvas实现压缩base64图片两种方法

一、具体代码 1、利用canvas压缩图片方法一 // 第一种压缩图片方法&#xff08;图片base64,图片类型,压缩比例,回调函数&#xff09;// 图片类型是指 image/png、image/jpeg、image/webp(仅Chrome支持)// 该方法对以上三种图片类型都适用 压缩结果的图片base64与原类型相同// …

02--微信小程序开发流程

开发小程序一般流程&#xff1a;申请小程序帐号安装小程序开发者工具开发小程序提交审核和发布1、注册小程序帐号在微信公众平台官网首页&#xff08;mp.weixin.qq.com&#xff09;点击右上角的“立即注册”按钮。2、填写帐号信息 主体为企业时需要一些信息包括&#xff1a;企业…

狂神说:面向对象(一) —— OOP与方法回顾

OOP详解以类的方式组织代码&#xff0c;以对象的方式组织&#xff08;封装&#xff09;数据什么是面向对象封装 【口袋装数据&#xff0c;留个口&#xff0c;可以用】继承 【儿子和父亲】多态 【同一个事物表现出多种形态】对象和类实际&#xff1a;先有对象后有类代码&#xf…

商城进货记录交易-课后程序(JAVA基础案例教程-黑马程序员编著-第七章-课后作业)

【实验7-2】商城进货记录交易 【任务介绍】 1.任务描述 每个商城都需要进货&#xff0c;而这些进货记录整理起来很不方便&#xff0c;本案例要求编写一个商城进货记录交易的程序&#xff0c;使用字节流将商场的进货信息记录在本地的csv文件中。程序具体要求如下&#xff1a; …

网络编程NIO

Java NIO&#xff08;New IO 或 Non Blocking IO&#xff09;是从Java 1.4 版本开始引入的一个新的IO API&#xff0c;可以替代标准的 Java IO API。NIO 支持面向缓冲区的、基于通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作。 非阻塞 IO(NIO) 通过Selector去实…

ASP.NET Core MVC 项目 IOC容器

目录 一&#xff1a;什么是IOC容器 二&#xff1a;简单理解内置Ioc容器 三&#xff1a;依赖注入内置Ioc容器 四&#xff1a;生命周期 五&#xff1a;多种注册方式 一&#xff1a;什么是IOC容器 IOC容器是Inversion Of Control的缩写&#xff0c;翻译的意思就是控制反转。 …

【Azure 架构师学习笔记】-Azure Data Factory (3)-触发器详解-翻转窗口

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Data Factory】系列。 接上文【Azure 架构师学习笔记】-Azure Data Factory (2)-触发器 前言 上文中提到触发器的类型有以下4种&#xff0c;其中第一种【计划】是常用的&#xff0c; 与其他工具/服务类似的方式&#…

游戏、广告作底盘,价值直播为引擎,搜狐活在当下

2022年&#xff0c;中国互联网行业迎来了集体性的“中年危机”。 流量见顶、红利耗尽&#xff0c;再加上疫情的影响&#xff0c;国内互联网企业在过去一年真真实实地感受到了寒气。根据工信部数据&#xff0c;2022年&#xff0c;中国规模以上互联网和相关服务企业总收入达1.46…

【异构图笔记,篇章1】RGCN:Modeling Relational Data with Graph Convolutional Networks

【异构图笔记&#xff0c;篇章1】RGCN:Modeling Relational Data with Graph Convolutional Networks论文信息论文要点快览论文内容介绍背景任务RGCN Conv的介绍RGCN的trick论文实验结果实体分类链路预测评价及总结本文仅供学习&#xff0c;未经同意请勿转载 后期会陆续公开关于…

顺序表的增删查改

数据结构 是数据存储的方式&#xff0c;对于不同的数据我们要采用不同的数据结构。就像交通运输&#xff0c;选用什么交通工具取决于你要运输的是人还是货物&#xff0c;以及它们的数量。 顺序存储结构 包括顺序表、链表、栈和队列等。 例如腾讯QQ中的好友列表&#xff0c;…

运动蓝牙耳机什么款式最好、公认最好用的运动耳机推荐

如今大家对于运动越来越热衷&#xff0c;健身意识的逐渐加强&#xff0c;也带动了对运动装备的需求&#xff0c;其中运动蓝牙耳机也成为运动达人不可缺少的一部分了&#xff0c;在运动的过程中增加点音乐元素进来也会增多点动力。所以市面上出现了各种款式不一的运动耳机&#…

渗透测试之局域网信息探测实验

渗透测试之局域网信息探测实验实验目的一、实验原理1.1 SoftPerfect Network Scanner 流量监控软件二、实验环境2.1 操作机器2.2 SoftPerfectNetscan Scanner三、实验步骤1. 解压并运行SoftPerfect Network Scanner软件2. 使用SoftPerfect Network Scanner进行局域网信息探测实…

并发编程学习篇从0-1合集

一、synchronized 一、原子性、有序性、可见性 1.1 原子性 数据库的事务&#xff1a;ACID A&#xff1a;原子性-事务是一个最小的执行的单位&#xff0c;一次事务的多次操作要么都成功&#xff0c;要么都失败。 并发编程的原子性&#xff1a;一个或多个指令在CPU执行过程中…

JVM 锁优化和逃逸分析详解

1 锁优化JVM 在加锁的过程中&#xff0c;会采用自旋、自适应、锁消除、锁粗化等优化手段来提升代码执行效率。1.1 自旋锁和自适应自旋现在大多的处理器都是多核处理器 &#xff0c;如果在多核心处理器&#xff0c;有让两个或者以上的线程并行执行&#xff0c;我们可以让一个等待…

(免费分享)基于ssm的BBS社区论坛系统带论文

项目描述前台部分:1.用户注册登录模块用户登录后,可以进行发帖回帖功能,在线签到功能,完善个人信息,添加好友,收藏贴子,评论帖子,点赞功能,记录功能(比如记录今天发生的事情)等等…2.排行榜模块1.帖子讨论热度排行,分两种排行方式:(1) 根据用户今日发出的帖子被回复数量进行排名…

Linux USB 开发指南

文章目录Linux USB 开发指南1 前言1.1 文档简介1.2 目标读者1.3 适用范围2 模块介绍2.1 模块功能介绍2.2 相关术语介绍2.3 模块配置介绍2.3.1 Device Tree 配置说明2.3.2 board.dts 配置说明2.3.3 kernel menuconfig 配置说明2.4 源码结构介绍2.5 驱动框架介绍2.6 Gadget 配置2…

MySQL体系结构及数据库引擎

文章目录一、MYSQL的体系结构1、连接器2、查询缓存3、分析器&#xff08;要做什么&#xff09;4、优化器&#xff08;怎么做&#xff09;5、执行器6、数据库引擎1&#xff09;mysql支持的引擎2&#xff09;常用的mysql引擎比较3&#xff09;索引组织表、堆组织表4&#xff09;内…