记忆化搜索CSP-S

目录

💡 核心思想

记忆化搜索是在递归搜索的基础上,将已计算过的子问题结果缓存起来,避免重复计算。本质上是动态规划的自顶向下实现方式。通常能把指数级复杂度优化为多项式级。

记忆化搜索是理解动态规划的最好跳板。很多同学觉得 DP 难,是因为自底向上的递推不好想。但如果你能用递归把问题想清楚,加上一个备忘录数组,就是记忆化搜索——和 DP 本质相同,只是写法不同。在 CSP 竞赛中,记忆化搜索和递推 DP 都可以,选你更顺手的。

🎯 直觉理解

记忆化搜索就是给递归"记笔记":同一个子问题算过一遍,就把结果存下来,下次直接查表,不再重复算。

以斐波那契为例:朴素递归算 $f(40)$ 要重复算 $f(30)$ 几千次;加一个数组缓存,每个值只算一次,复杂度从 $O(2^n)$ 降到 $O(n)$。

记忆化搜索 = 递归 + 缓存,本质是"自顶向下"的动态规划。写法上只要:函数加一个记录数组,算之前先查、算完就存。它比递推版 DP 更好写,因为状态转移方向自然。

📝 算法流程

  1. 写出朴素的递归搜索
  2. 添加备忘录(数组或map)存储已计算结果
  3. 递归入口先查表:算过直接返回
  4. 没算过则递归计算,存入备忘录后返回

$$\text{朴素斐波那契: } O(2^n) \to \text{记忆化: } O(n)$$

📊 复杂度分析

指标复杂度
时间等于子问题数量 × 每个子问题耗时
空间等于子问题数量

💻 参考实现(C++)

C++ (C++17)
#include 
using namespace std;
long long memo[1005];
long long fib(int n) {
    if (n <= 1) return n;
    if (memo[n] != -1) return memo[n]; // 查表
    return memo[n] = fib(n-1) + fib(n-2); // 计算+存储
}
int main() {
    memset(memo, -1, sizeof(memo));
    int n; cin >> n;
    cout << fib(n) << endl;
    return 0;
}

⚠️ 常见坑点

备忘录初始化值要和合法答案区分开(如用 -1 表示未计算)

状态设计不合理导致子问题数量爆炸

递归深度过大——可能需要改写为迭代 DP

用 map 做备忘录时常数较大

📚 相关题目

题目来源难度备注
P1048 采药洛谷CSP-J记忆化/01背包
P1434 滑雪洛谷CSP-S记忆化搜索经典题