定义

记忆化搜索是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。

因为记忆化搜索确保了每个状态只访问一次,它也是一种常见的动态规划实现方式。

引入

题目见[NOIP2005] 采药

朴素的 DFS 做法

很容易实现这样一个朴素的搜索做法:在搜索时记录下当前准备选第几个物品、剩余的时间是多少、已经获得的价值是多少这三个参数,然后枚举当前物品是否被选,转移到相应的状态。

int n, t;
int tcost[103], mget[103];
int ans = 0;void dfs(int pos, int tleft, int tans) {if (tleft < 0) return;if (pos == n + 1) {ans = max(ans, tans);return;}dfs(pos + 1, tleft, tans);dfs(pos + 1, tleft - tcost[pos], tans + mget[pos]);
}int main() {cin >> t >> n;for (int i = 1; i <= n; i++) cin >> tcost[i] >> mget[i];dfs(1, t, 0);cout << ans << endl;return 0;
}

这种做法的时间复杂度是指数级别的,并不能通过本题。

优化

上面的做法为什么效率低下呢?因为同一个状态会被访问多次。

如果我们每查询完一个状态后将该状态的信息存储下来,再次需要访问这个状态就可以直接使用之前计算得到的信息,从而避免重复计算。这充分利用了动态规划中很多问题具有大量重叠子问题的特点,属于用空间换时间的「记忆化」思想。

具体到本题上,我们在朴素的 DFS 的基础上,增加一个数组 mem 来记录每个 dfs(pos,tleft) 的返回值。刚开始把 mem 中每个值都设成 -1(代表没求解过)。每次需要访问一个状态时,如果相应状态的值在 mem 中为 -1,则递归访问该状态。否则我们直接使用 mem 中已经存储过的值即可。

通过这样的处理,我们确保了每个状态只会被访问一次。

int n, t;
int tcost[103], mget[103];
int mem[103][1003];int dfs(int pos, int tleft) {if (mem[pos][tleft] != -1)return mem[pos][tleft];  // 已经访问过的状态,直接返回之前记录的值if (pos == n + 1) return mem[pos][tleft] = 0;int dfs1, dfs2 = -INF;dfs1 = dfs(pos + 1, tleft);if (tleft >= tcost[pos])dfs2 = dfs(pos + 1, tleft - tcost[pos]) + mget[pos];  // 状态转移return mem[pos][tleft] = max(dfs1, dfs2);  // 最后将当前状态的值存下来
}int main() {memset(mem, -1, sizeof(mem));cin >> t >> n;for (int i = 1; i <= n; i++) cin >> tcost[i] >> mget[i];cout << dfs(1, t) << endl;return 0;
}

与递推的联系与区别

在求解动态规划的问题时,记忆化搜索与递推的代码,在形式上是高度类似的。这是由于它们使用了相同的状态表示方式和类似的状态转移。也正因为如此,一般来说两种实现的时间复杂度是一样的。

下面给出的是递推实现的代码(为了方便对比,没有添加滚动数组优化),通过对比可以发现二者在形式上的类似性。

const int maxn = 1010;
int n, t, w[105], v[105], f[105][1005];int main() {cin >> n >> t;for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];for (int i = 1; i <= n; i++)for (int j = 0; j <= t; j++) {f[i][j] = f[i - 1][j];if (j >= w[i])f[i][j] = max(f[i][j], f[i - 1][j - w[i]] + v[i]);  // 状态转移方程}cout << f[n][t];return 0;
}

在求解动态规划的问题时,记忆化搜索和递推,都确保了同一状态至多只被求解一次。而它们实现这一点的方式则略有不同:递推通过设置明确的访问顺序来避免重复访问,记忆化搜索虽然没有明确规定访问顺序,但通过给已经访问过的状态打标记的方式,也达到了同样的目的。

与递推相比,记忆化搜索因为不用明确规定访问顺序,在实现难度上有时低于递推,且能比较方便地处理边界情况,这是记忆化搜索的一大优势。但与此同时,记忆化搜索难以使用滚动数组等优化,且由于存在递归,运行效率会低于递推。因此应该视题目选择更适合的实现方式。

如何写记忆化搜索

方法一

  1. 把这道题的 dp 状态和方程写出来
  2. 根据它们写出 dfs 函数
  3. 添加记忆化数组

转为:

int dfs(int i) {if (mem[i] != -1) return mem[i];int ret = 1;for (int j = 1; j < i; j++)if (a[j] < a[i]) ret = max(ret, dfs(j) + 1);return mem[i] = ret;
}int main() {memset(mem, -1, sizeof(mem));// 读入部分略去int ret = 0;for (int j = 1; j <= n; j++) {ret = max(ret, dfs(j));}cout << ret << endl;
}

方法二

  1. 写出这道题的暴搜程序(最好是 dfs)
  2. 将这个 dfs 改成「无需外部变量」的 dfs
  3. 添加记忆化数组

记忆化搜索 (蒟蒻练习生)相关推荐

  1. BZOJ1415 [Noi2005]聪聪和可可 【SPFA + 期望dp记忆化搜索】

    题目 输入格式 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行 ...

  2. 【DP】【记忆化搜索】NIKOLA(jzoj 1150)

    NIKOLA 题目大意: NIKOLA画了一排数字,他一开始在1,他可以往前跳T+1格(T为上一次跳到此格跳的格数),或往后T格(T一开始为0),但不能跳出界,没跳到一个格子,就要加上此格子的值(一开 ...

  3. 洛谷P1057 传球游戏(记忆化搜索)

    点我进入题目 题目大意:n个小孩围一圈传球,每个人可以给左边的人或右边的人传球,1号小孩开始,一共传m次,请问有多少种可能的路径使球回到1号小孩. 输入输出:输入n,m,输出路径的数量. 数据范围:4 ...

  4. 图论 ---- F. Graph Traveler 记忆化搜索 + 思维预处理(数论同余恒等式)

    题目链接 题目大意: q∈[1,1e5],n∈[1,1000],mi∈[1,10]q\in[1,1e5],n\in[1,1000],m_i\in[1,10]q∈[1,1e5],n∈[1,1000],m ...

  5. 思维dp ---- Codeforces Round #711 (Div. 2) - C. Planar Reflections[dp/记忆化搜索]

    题目链接 题目大意: 就是给你n个平面和一个寿命为k的衰变粒子.开始粒子从左向右飞行,粒子每经过一个平面就会产生一个副本粒子,这个副本粒子比原粒子的寿命少1,即为k-1,并且飞行方向是原粒子的反方向. ...

  6. Discovering Gold LightOJ - 1030[概率dp或者记忆化搜索]

    题目大意:有一个[1,n][1,n][1,n]的数轴,数轴上的每个对应位置上都有金矿,你初始位置是1,然后你每次都会投色子决定你下一步跳到哪里,如果你跳出了nnn,那么你就要重新投.问你跳到nnn的时 ...

  7. BZOJ 1589 Trick or Treat on the Farm (tarjan缩点,记忆化搜索)[Usaco 2008 Dec Gold]【BZOJ计划】

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 Weblink https://hydro.ac/d/bzoj/p/1589 Problem 每年万圣 ...

  8. UVA437 巴比伦塔 The Tower of Babylon(矩形嵌套进阶版、DAG上DP、记忆化搜索)

    整理的算法模板合集: ACM模板 本题和基础的矩形覆盖差不多,只不过变成了三维的长方形. 因为每次转移的时候只有顶面的x和y会影响决策的进行,所以我们只需要用一个二元组(a,b)(a, b)(a,b) ...

  9. [P1434 [SHOI2002]滑雪](DFS,记忆化搜索)

    P1434 [SHOI2002]滑雪 题目描述 Michael喜欢滑雪.这并不奇怪,因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你 ...

最新文章

  1. PCL点云特征描述与提取(1)
  2. 国内数十位大佬合作,综述预训练模型的过去、现在与未来
  3. ASP.NET MVC 4 (十一) Bundles和显示模式
  4. R语言CRAN软件包Meta分析
  5. Mongodb知识总结
  6. linux7配置dns服务器,centOS7搭建DNS服务器配置详解
  7. LeetCode 350. Intersection of Two Arrays II
  8. [Ext JS6]工作区-Workspace
  9. 动画图解:十大经典排序算法动画与解析
  10. oninput和onpropertychange以及其兼容处理
  11. Adobe Creative Cloud官方卸载工具
  12. 小程序云函数实现微信支付如此简单
  13. 金田变频器说明书_金田BH386系列变频器使用手册.pdf
  14. vue 动态加载阿里云字体图标库
  15. Android 蓝牙驱动专题分析(2)--- 蓝牙驱动代码流程、kernel dump、tombstone问题分析
  16. 视频编解码学习之四:视频处理及编码标准
  17. windows server 2008磁盘管理
  18. sed命令详解(下)
  19. chipmunk创建和销毁刚体过程
  20. 文科专业计算机等级吗,文科生如何过计算机等级考试

热门文章

  1. MATLAB数组相除
  2. 计算机有关书籍学习与推荐
  3. eclipse启动慢解决方法
  4. Larry wall - Perl之父
  5. 2W字!梳理50道经典计算机网络面试题(收藏版)
  6. matlab中repmat函数使用举例解释(会识字就能看懂)
  7. MySQL语句的条件查询
  8. 利用snpEff对基因型VCF文件进行变异注释的详细方法
  9. mysql 实例名是什么意思_mysql 实例是什么意思?
  10. C语言第一课:初始C语言