跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
|注册
博客列表

目录

  1. 记忆化搜索 vs 动态规划
  2. 斐波那契数
  3. 题解
  4. 代码
  5. 不同路径
  6. 题解
  7. 代码
  8. 最长递增子序列
  9. 题解
  10. 代码
  11. 猜数字大小 II
  12. 题解
  13. 代码
  14. 矩阵中的最长递增路径
  15. 题解
  16. 代码
  17. 总结
C++算法

记忆化搜索与动态规划刷题总结

本文对比了记忆化搜索与动态规划的核心思想,通过斐波那契数、不同路径、最长递增子序列、猜数字大小 II 及矩阵最长递增路径五个经典案例,演示了如何通过添加备忘录将暴力 DFS 优化为高效解法。文章详细阐述了备忘录的设计方法、递归终止条件及状态转移逻辑,并提供了完整的 C++ 代码实现,帮助读者理解从 O(2^n) 到 O(n) 的复杂度优化过程。

协议工匠发布于 2026/3/29更新于 2026/4/131 浏览
记忆化搜索与动态规划刷题总结

记忆化搜索 vs 动态规划

  1. 记忆化搜索:有完全相同的问题/数据保存起来,带有备忘录的递归
  2. 记忆化搜索的步骤:
    1. 添加一个备忘录 <可变参数,返回值>
    2. 递归每次返回的时候,将结果放入备忘录中
    3. 在每次进入递归的时候,往备忘录中查找一下
  3. 记忆化搜索和常规的动态规划都是动态规划,暴搜 -> 记忆化搜索 -> 动态规划
  4. 有大量重复的问题才能改成记忆化搜索

斐波那契数

题目链接

题解

  1. 创建一个备忘录
  2. 把备忘录中的值初始化为斐波那契数中不可能出现的值 -1
  3. 在每次递归完成之前,把值存入备忘录中
  4. 在每次进入递归的时候,查找一下备忘录中是否有值,有值就直接返回,相当于剪枝

代码

#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

// 记忆化搜索
class Solution {
public:
    int memo[31];
    
    int fib(int n) {
        memset(memo, -1, sizeof memo);
        return dfs(n);
    }
    
    int dfs(int n) {
        if (memo[n] != -1) {
            return memo[n];
        }
        if (n == 0 || n == ) {
            memo[n] = n;
             n;
        }
        memo[n] = (n - ) + (n - );
         memo[n];
    }
};


  {
:
     dp[];
    
    {
        dp[] = , dp[] = ;
         ( i = ; i <= n; i++) {
            dp[i] = dp[i - ] + dp[i - ];
        }
         dp[n];
    }
};
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog

更多推荐文章

查看全部
  • AI Agent Skills 资源合集:支持 Cursor、Claude Code 等工具
  • Whisper-large-v3 云端部署详细步骤
  • Qwen2.5-7B-Instruct 工具调用集成心知天气示例
  • FPGA 数字运算与控制:浮点数实现与 PID 算法
  • 基于 DeepSeek 与 Neo4j 知识图谱的企业级 RAG 架构演进
  • 2026 年 3 月全球大模型全景:国产登顶、百万上下文与智能体爆发
  • Video2Robot:从视频到机器人动作的端到端生成管道

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown 转 HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online

  • HTML 转 Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

1
return
dfs
1
dfs
2
return
// 动态规划
class
Solution
public
int
31
int fib(int n)
0
0
1
1
for
int
2
1
2
return

不同路径

题目链接

题解

  1. 函数头的设计,只需要 i 和 j 的坐标
  2. 返回条件:i == 0 || j == 0 都是返回 0;i == 1 && j == 1 第一个点返回 1
  3. 记忆化搜索就是创建一个二维的备忘录,在递归之前往备忘录中查找一下,返回之前把结果都存在备忘录中

代码

#include <vector>
using namespace std;

// 记忆化搜索
class Solution {
public:
    int memo[102][102];
    
    int uniquePaths(int m, int n) {
        return dfs(m, n);
    }
    
    int dfs(int m, int n) {
        // 往备忘录中查找一下
        if (memo[m][n]) return memo[m][n];
        
        // 返回条件
        if (m == 0 || n == 0) return 0;
        // 第一个点
        if (m == 1 && n == 1) {
            memo[m][n] = 1;
            return 1;
        }
        memo[m][n] = dfs(m - 1, n) + dfs(m, n - 1);
        return memo[m][n];
    }
};

// 动态规划
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        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];
    }
};

最长递增子序列

题目链接

题解

  1. 记忆化搜索:每次枚举以 pos 位置为起点的最长递增子序列,让 pos+1 位置的值和 pos 位置比较大小,如果大于就加一,函数头需要 nums 和 pos 位置,函数体需要 pos+1 位置到 n-1 位置,dfs 会完成找到最大递增子序列的工作,+1 是加上本身
  2. 动态规划:从后往前添加状态,第一个循环用来枚举位置,第二个循环用来比较大小,更新最长递增子序列到 dp[i] 中,内层循环结束,更新一下最长的子序列,因为不知道哪个位置是最大的

代码

#include <vector>
#include <algorithm>
using namespace std;

// 记忆化搜索
class Solution {
public:
    int memo[2510];
    
    int lengthOfLIS(vector<int>& nums) {
        int ret = 1;
        // 每次以 i 位置为起点往后搜索
        for (int i = 0; i < nums.size(); i++) {
            ret = max(dfs(nums, i), ret);
        }
        return ret;
    }
    
    int dfs(vector<int>& nums, int pos) {
        if (memo[pos] != 0) return memo[pos];
        int ret = 1;
        for (int i = pos + 1; i < nums.size(); i++) {
            if (nums[i] > nums[pos]) ret = max(ret, dfs(nums, i) + 1);
        }
        memo[pos] = ret;
        return memo[pos];
    }
};

// 动态规划
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        // 最短的子序列都是 1
        vector<int> dp(n, 1);
        int ret = 1;
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 1; j < n; j++) {
                if (nums[j] > nums[i]) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

猜数字大小 II

题目链接

题解

  1. 暴搜 -> 记忆化搜索
  2. 算法原理:在区间 [1,n] 固定一个点 i,分为左右区间,寻找花费最小金钱达到必胜的方案,方案数是不止实例一那一种的,然后在左右枝中寻找所花费金额的最大值才能胜利
  3. 函数头需要传参左右区间,函数体需要实现寻找多种方案中的最小花费和当前方案下的最大金钱,出现了重复的数据可以使用记忆化搜索

代码

#include <climits>
#include <algorithm>
using namespace std;

class Solution {
public:
    int memo[201][201];
    
    int getMoneyAmount(int n) {
        // 计算出所有方案数中的最小花费
        return dfs(1, n);
    }
    
    int dfs(int left, int right) {
        if (left >= right) return 0;
        if (memo[left][right] != 0) return memo[left][right];
        
        int ret = INT_MAX;
        for (int head = left; head <= right; head++) {
            int x = dfs(left, head - 1);
            int y = dfs(head + 1, right);
            ret = min(ret, max(x, y) + head);
        }
        memo[left][right] = ret;
        return ret;
    }
};

矩阵中的最长递增路径

题目链接

题解

  1. 算法原理:从一个点开始 dfs,暴力枚举出每一个递增的路径,返回其中最长的路径即可
  2. dfs 函数的设计,需要 i,j 坐标去搜索每一个位置
  3. 记忆化数组,如果出现相同的路径,不用再次 dfs,直接返回即可

代码

#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    int memo[201][201];
    int m, n;
    
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        int ret = 1;
        m = matrix.size(), n = matrix[0].size();
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                ret = max(ret, dfs(matrix, i, j));
            }
        }
        return ret;
    }
    
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    
    int dfs(vector<vector<int>>& matrix, int i, int j) {
        if (memo[i][j]) return memo[i][j];
        int ret = 1;
        for (int k = 0; k < 4; k++) {
            int x = dx[k] + i, y = dy[k] + j;
            if (x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]) {
                ret = max(ret, dfs(matrix, x, y) + 1);
            }
        }
        memo[i][j] = ret;
        return ret;
    }
};

总结

  1. 记忆化搜索就是先把暴力的 dfs 先写出来,然后加一个备忘录优化
  2. 记忆化搜索也和常规的动态规划是一样的,即记忆化搜索也可以改为动态规划的代码
  3. 出现大量重复的数据可以使用记忆化搜索,记忆化搜索可以使原本 O(2^n) 时间复杂度降为 O(n)
  • 无需插件即可在 Copilot 中接入第三方 OpenAI 接口
  • GitHub Copilot Pro 免费权益获取指南与功能对比
  • 基于 YOLOv11 系列的电动自行车违规载人检测系统开发实践
  • Xilinx Clocking Wizard IP 核完全指南:基础到高级应用
  • Lottie-Web 完整技术指南
  • MoltBot 机器人集成钉钉 Stream 流式接入配置指南
  • Vue 项目服务器部署指南
  • 飞书 CLI 开源:让 AI 真正接管你的飞书全流程
  • OpenClaw 单实例配置多 Agent、多 QQ 及飞书机器人
  • FPGA 实现 CIC 抽取滤波器原理与 Verilog 代码
  • Moyin Creator(魔因漫创):AI 影视生产级全流程创作工具
  • 英伟达 GTC 2026 发布新推理芯片与 Rubin 架构
  • OpenClaw 钉钉群聊多机器人配置指南