记忆化搜索——AcWing 901. 滑雪

记忆化搜索

定义

记忆化搜索是一种结合了搜索和动态规划思想的方法。它通过将已经计算过的结果存储起来,在后续遇到相同情况时直接返回存储的结果,避免重复计算。

运用情况

  • 当问题可以用递归方式求解,但存在大量重复计算时。
  • 一些复杂的组合或搜索问题,通过记忆化来提高效率。

注意事项

  • 合理设计存储结构:用于保存已经计算过的状态和结果。
  • 确保正确更新和检索记忆的数据。
  • 注意边界情况和特殊情况的处理,以保证记忆化的准确性。

解题思路

  • 确定问题的递归结构和状态表示。
  • 在递归函数中,先检查当前状态是否已经被记忆,如果是则直接返回记忆的结果。
  • 如果没有记忆,则进行正常的计算,并将结果存储起来。
  • 按照问题的逻辑进行递归调用和结果处理。

记忆化搜索能够有效地减少重复计算,提高算法的效率,在很多情况下是一种非常实用的技术。

AcWing 901. 滑雪   

题目描述

901. 滑雪 - AcWing题库

运行代码

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int R = 300;
const int C = 300;
int r, c;
int grid[R][C];
int dp[R][C];
int dfs(int x, int y) {
    if (dp[x][y]!= -1) {
        return dp[x][y];
    }
    int maxLen = 1;
    if (x > 0 && grid[x - 1][y] < grid[x][y]) {
        maxLen = max(maxLen, 1 + dfs(x - 1, y));
    }
    if (x < r - 1 && grid[x + 1][y] < grid[x][y]) {
        maxLen = max(maxLen, 1 + dfs(x + 1, y));
    }
    if (y > 0 && grid[x][y - 1] < grid[x][y]) {
        maxLen = max(maxLen, 1 + dfs(x, y - 1));
    }
    if (y < c - 1 && grid[x][y + 1] < grid[x][y]) {
        maxLen = max(maxLen, 1 + dfs(x, y + 1));
    }
    dp[x][y] = maxLen;
    return maxLen;
}
int main() {
    cin >> r >> c;
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            cin >> grid[i][j];
        }
    }
    memset(dp, -1, sizeof(dp));
    int Length = 0;
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            Length = max(Length, dfs(i, j));
        }
    }
    cout << Length << endl;
    return 0;
}

代码思路

  1. 初始化和输入:

    • 定义了常量R和C来表示二维网格的最大行数和列数。
    • 使用cin读取实际的行数r和列数c。
    • 通过双重循环读取每个格子grid[i][j]的值。
  2. 记忆化搜索(DFS + 动态规划):

    • 定义一个dp数组,dp[i][j]表示以grid[i][j]为起点的最长递增路径长度。初始化为-1,表示尚未计算。
    • 实现深度优先搜索(DFS)函数dfs(x, y),它计算以(x, y)为起点的最长递增路径长度。
      • 首先,如果当前位置的最长路径已计算过,则直接返回dp[x][y]。
      • 然后,初始化当前路径长度为1(至少包含当前位置)。
      • 探索四个方向(上、下、左、右),如果相邻格子的值小于当前位置的值,则递归调用dfs,并累加路径长度,取所有可能路径中的最大值。
      • 最后,将计算结果存入dp[x][y]并返回。
  3. 计算并输出最长路径长度:

    • 遍历整个网格,对每个格子调用dfs函数,更新全局变量Length为所有起点的最长路径中的最大值。
    • 输出这个最大长度。

总结:这段代码通过结合深度优先搜索(DFS)和动态规划(记忆化搜索)的方法,有效地解决了在一个二维网格上寻找最长递增路径的问题。记忆化搜索避免了重复计算,提高了算法效率。

改进思路

  1. 剪枝优化:在进行DFS时,可以在判断相邻格子是否符合条件(即值大于当前格子)之前,先检查该格子是否已经被访问过(即dp值是否已经计算过),这样可以减少不必要的递归调用,进一步优化时间复杂度。

  2. 使用迭代法替代递归:对于深度很大的网格,递归可能导致栈溢出。可以考虑使用栈或队列实现广度优先搜索(BFS)或迭代深度优先搜索(迭代DFS),以减少递归调用的深度。

  3. 空间优化:若内存限制严格,可以考虑使用滚动数组或其它空间优化策略来减少dp数组的大小。例如,只需保留上一行或上一列的dp值,因为计算新的一行或列时只需要用到这些信息。

  4. 输入验证:在读取r和c时,可以加入对输入值的校验,确保它们在预设的网格大小范围内,增加程序的健壮性。

  5. 代码注释和可读性:增加详细的注释,尤其是对于算法的核心逻辑部分,以便于他人理解和维护。同时,对变量命名可以更加直观,如将Length改为maxPathLength以提高代码的可读性。

  6. 泛化处理:如果考虑代码的通用性,可以将R和C的常量定义移至函数参数中,使得算法能灵活适应不同大小的网格,提高代码的复用性。

  7. 并行计算:对于大规模的网格,可以探索并行计算的可能性,比如使用OpenMP或其他并行计算框架来加速计算各个起始点的最长路径长度。

改进代码(AI)

#include <iostream>
#include <vector>
#include <cstring>
#include <stack>
using namespace std;

const int R = 300;
const int C = 300;
int r, c;
int grid[R][C];
int dp[R][C];
int dx[] = {-1, 1, 0, 0}; // 方向数组,分别对应上、下、左、右
int dy[] = {0, 0, -1, 1};

int dfsIterative(int x, int y) {
    stack<pair<int, int>> stk; // 使用栈模拟深度优先搜索
    stk.push({x, y});
    int maxLength = 1;
    dp[x][y] = 1; // 初始化已访问的格子长度为1

    while (!stk.empty()) {
        auto [curX, curY] = stk.top(); stk.pop();
        for (int dir = 0; dir < 4; ++dir) {
            int newX = curX + dx[dir];
            int newY = curY + dy[dir];
            if (newX >= 0 && newX < r && newY >= 0 && newY < c && 
                grid[newX][newY] > grid[curX][curY] && (dp[newX][newY] == -1 || dp[newX][newY] < dp[curX][curY] + 1)) {
                dp[newX][newY] = dp[curX][curY] + 1;
                maxLength = max(maxLength, dp[newX][newY]);
                stk.push({newX, newY});
            }
        }
    }
    return maxLength;
}

int main() {
    cin >> r >> c;
    for (int i = 0; i < r; ++i) {
        for (int j = 0; j < c; ++j) {
            cin >> grid[i][j];
        }
    }
    memset(dp, -1, sizeof(dp)); // 初始化dp数组为-1
    int maxPathLength = 0;
    for (int i = 0; i < r; ++i) {
        for (int j = 0; j < c; ++j) {
            if (dp[i][j] == -1) { // 避免重复计算
                maxPathLength = max(maxPathLength, dfsIterative(i, j));
            }
        }
    }
    cout << maxPathLength << endl;
    return 0;
}

其它代码

#include <iostream>
#include <cstring> 
using namespace std;
const int N = 310;
int n, m;
int h[N][N];
int f[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dfs(int x, int y)
{
    int &v = f[x][y];
    if(~v) return v;
    v = 1;
    for(int i = 0; i < 4; i++)
    {
        int a = x + dx[i], b = y + dy[i];
        if(a >= 1 && a <= n && b >= 1 && b <= m && h[a][b] < h[x][y])
             v = max(v, dfs(a, b) + 1);
    }
    return v;
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        for(int  j = 1; j <= m; j++)
            cin >> h[i][j];
    int res = 0;
    memset(f, -1, sizeof f);
    for(int i = 1; i <= n; i++)
        for(int  j = 1; j <= m; j++)
            res = max(res, dfs(i, j));
    cout <<res <<endl;
    return 0;
}

代码思路

  • 定义了一些常量和变量:
    • N 表示最大的行列数。
    • n 和 m 表示实际的行数和列数。
    • h[N][N] 存储矩阵中每个位置的高度值。
    • f[N][N] 用于记忆化已经计算过的最长滑雪长度。
  • dx 和 dy 数组定义了四个方向的移动偏移量。
  • dfs 函数是核心的搜索函数:
    • 通过引用获取 f[x][y],如果已经计算过(不为 -1),则直接返回。
    • 初始化当前位置的最长长度为 1
    • 遍历四个方向,检查新位置是否合法且高度小于当前位置,如果是,则递归调用 dfs 并更新当前位置的最长长度为递归结果加 1 的最大值。
  • 在 main 函数中:
    • 输入矩阵的行数和列数以及每个位置的高度。
    • 初始化 f 数组为 -1
    • 通过两层循环对每个位置调用 dfs 函数,获取最长长度,并不断更新结果 res
    • 最后输出最长长度。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity核心

回顾 Unity核心学习的主要内容 项目展示 基础知识 认识模型制作流程 2D相关 图片导入设置相关 图片导入概述 参数设置——纹理类型 参数设置——纹理形状 参数设置——高级设置 参数设置——平铺拉伸 参数设置——平台设置&#xff08;非常重要&#xff09; Sprite Sprite Edit…

JavaSE 面向对象程序设计进阶 抽象类和接口 2024年详解

目录 抽象类 抽象方法 抽象类和抽象方法的注意事项 ​编辑 接口 如何定义接口 注意 代码实现 ​编辑 接口中的成员特点 接口和类之间的关系 1.类与类的关系 2.类与接口的关系 3.接口与接口的关系 ​编辑 拓展 接口中的默认方法 接口中的静态方法 ​编辑 接口…

网站配置https,购买ssl证书,配置域名,配置nginx,实现安全访问

文章目录 前言一. 域名1. 购买域名2. 添加记录 二. ssl证书创建测试证书购买正式证书证书签发证书申请提交审核DNS验证审核通过下载证书 三. 部署nginx上传ssl证书配置nginx.conf文件找到nginx.conf文件编辑nginx.conf文件重启nginx 前言 当你创建了一个网站后&#xff0c;通过…

73. UE5 RPG 优化投射物以及敌人生成

解决发射物会与地面产生交互的问题 之前一直遇到发射物的体积过大会在发射时&#xff0c;和地面产生交互&#xff0c;我们可以调整小一些&#xff0c;然后为了防止它和自身产生交互事件。我们可以实现它在生成后&#xff0c;不会触发相关事件&#xff0c;而是在一定时间后。 对…

振弦式渗压计在土木工程安全监测中的重要性解析

在土木工程领域中&#xff0c;特别是涉及到坝体、隧道、路基等复杂结构的监测与安全管理时&#xff0c;渗压计作为一种关键的测量工具&#xff0c;发挥着举足轻重的作用。其中&#xff0c;振弦式渗压计以其独特的优点&#xff0c;得到了广泛的应用和认可。本文将对振弦式渗压计…

一文读懂 HTTP 和 RPC 的区别

随着互联网技术的发展&#xff0c;网络通信在各种应用中扮演着至关重要的角色。无论是构建 Web 应用还是进行服务之间的交互&#xff0c;选择合适的通讯协议成为开发者们需要深入思考的问题。在众多协议中&#xff0c;HTTP&#xff08;HyperText Transfer Protocol&#xff09;…

【IEEE ACCESS】论文发表记录 2

上次发IEEE ACCESS 感觉不错&#xff0c;速度较快&#xff0c;审稿费也不太夸张&#xff0c;这次梅开二度&#xff0c;希望好运。 官网&#xff1a;IEEE Access: The Multidisciplinary Open Access Journal 期刊水平&#xff1a; 范围认证 -中国科学院文献情报中心期刊分区表…

VBA学习(13):获取多层文件夹内文件名并建立超链接

代码使用了FileSystemObject对象和递归的方法实现文件夹和文件的遍历功能。分别将文件夹名称和文件名提取在表格的A/B列&#xff0c;并对文件名创建了超链接。 示例代码如下&#xff1a; Sub AutoAddLink()Dim strFldPath As StringWith Application.FileDialog(msoFileDialog…

好用的便签是什么 电脑桌面上好用的便签

作为一名文字工作者&#xff0c;我经常需要在繁杂的思绪中捕捉灵感&#xff0c;记录下那些一闪而过的想法。在寻找一款适合电脑桌面的便签应用时&#xff0c;我偶然发现了敬业签便签软件简直是为我量身定制的&#xff0c;它不仅界面简洁&#xff0c;操作便捷&#xff0c;更重要…

中国计算机学会芯片大会 (CCF Chip 2024)

&#x1f31f; 中国计算机学会芯片大会(CCF Chip Conference&#xff0c;简称&#xff1a;CCF Chip) 将于&#x1f4c5; 2024年7月19日至21日在上海市松江区上海富悦大酒店召开。 &#x1f389; #CCF Chip 2024# 主题前瞻&#xff1a;"发展芯技术&#xff0c;智算芯未来&q…

【Java】已解决java.net.HttpRetryException异常

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例 已解决java.net.HttpRetryException异常 在Java的网络编程中&#xff0c;尤其是使用Apache HttpClient或其他类似的HTTP客户端库时&#xff0c;可能会遇到java.net.HttpRetryException异常。这个…

MMDetection 目标检测 —— 环境搭建和基础使用

参考文档 开始你的第一步 — MMDetection 3.3.0 文档 依赖 步骤 0. 下载并安装 Anaconda。 步骤 1. 创建并激活一个 conda 环境。&#xff08;我选择的是python3.10&#xff09; conda create --name openmmlab python3.8 -y conda activate openmmlab 步骤 2. 基于 PyTo…

Spring Boot基础入门

引言 Spring Boot是一个开源的Java框架&#xff0c;旨在简化Spring应用程序的创建和部署过程。它提供了一种快速和简便的方式来创建独立的、生产级别的基于Spring的应用程序。本文将介绍Spring Boot的基础知识&#xff0c;包括其核心特性、如何开始使用Spring Boot以及构建你的…

将强化学习重新引入 RLHF

我们很高兴在 TRL 中介绍 RLOO (REINFORCE Leave One-Out) 训练器。作为一种替代 PPO 的方法&#xff0c;RLOO 是一种新的在线 RLHF 训练算法&#xff0c;旨在使其更易于访问和实施。特别是&#xff0c; RLOO 需要的 GPU 内存更少&#xff0c;并且达到收敛所需的挂钟时间也更短…

01 Shell编程规范与变量

1、Shell脚本概述 在一些复杂的Linux维护工作中&#xff0c;大量的重复性的输入和交互操作不仅费力费时&#xff0c;而且容易出错&#xff0c;而编写一个恰到好处的Shell脚本程序&#xff0c;可以批量处理、自动化地完成一系列维护任务&#xff0c;大大减轻管理员的负担。 Sh…

【CT】LeetCode手撕—54. 螺旋矩阵

目录 题目1- 思路2- 实现⭐54. 螺旋矩阵——题解思路 3- ACM实现 题目 原题连接&#xff1a;92. 反转链表 II 1- 思路 模式识别&#xff1a;螺旋矩阵 ——> 用四个指针来顺时针遍历 2- 实现 ⭐54. 螺旋矩阵——题解思路 class Solution {public List<Integer> spir…

RuoYi Swagger请求401

问题描述&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 使用ruoyi-vue分离版&#xff0c;访问swagger&#xff0c;发现接口都调用失败&#xff1a;401 解决方案&#xff1a; 最终解决问题如下步骤&#xff1a; 1、 调用swagger中的接口&#xff0c;报错&a…

QT MQTT (二)编译与集成

一、QT MQTT 提供 MQTT 客户端服务的 Qt 专用库基于标准化发布 / 订阅协议&#xff0c;用于在设备和组件之间可靠地共享数据。MQTT 是为保证状态正确性、满足高安全标准和交换最小数据而设计的协议&#xff0c;因此被广泛应用于各种分布式系统和物联网解决方案中。 Qt开发MQT…

修改源码,打patch包,线上环境不生效

1.首先看修改的源码文件是否正确 在node_modules中&#xff0c;找对应的包&#xff0c;然后查看包中package.json 的main和module。如果用require引入&#xff0c;则修改lib下面的组件&#xff0c;如果是import引入则修改es下面的文件 main 对应commonjs引入方式的程序入口文件…

WPF 数据分组显示

WPF 数据分组显示 效果展示&#xff1a; Student类&#xff1a; public class Student {public string Name { get; set; }public string Class { get; set; }public int Age { get; set; } }MainWindow.xaml.cs public partial class MainWindow : Window {private Observ…