C++记忆化搜索

前言(一些小废话)

C++中的记忆化搜索(Memoization)是一种优化技术,用于减少重复计算的开销。它常用于动态规划和递归问题中。
记忆化搜索和动态规划从根本上来讲就是一个东西,任何一个DP方程都能转为记忆化搜索 ,反之亦然。
我写这篇文章,是因为自己的DP基础薄弱,不易推出状态转移方程。实际上,DP的代码量更少,也更方便(在能推出状态转移方程的基础上)。

记忆化搜索的优缺点

优点

  1. 记忆化搜索可以避免搜到无用状态, 特别是在有状态压缩时
  2. 不需要注意转移顺序(这里的"转移顺序"指正常DP中for循环的嵌套顺序以及循环变量是递增还是递减)
  3. 边界情况非常好处理, 且能有效防止数组访问越界
  4. 对我这种蒟蒻来说写起来简单易懂
  5. 有些DP(如区间DP)用记忆化搜索写很简单但正常DP很难

缺点

  1. 不能滚动数组(虽然我也不大会),要滚动数组的话还是老老实实写DP吧
  2. 有些优化比较难加
  3. 由于递归, 有时效率较低但不至于 TLE (状压dp除外)
  4. 代码有点长

如何写记忆化搜索

不考虑DP
由暴搜开始思考

  1. 写出这道题的暴搜程序
  2. 将这个DFS改成"无需外部变量"的DFS
  3. 添加记忆化数组

例题:[NOIP2005 普及组] 采药

假设我P也不会,只会暴搜,你会得到:

#include<bits/stdc++.h>
using namespace std;
#define N 105
int n,t;
int T[N],W[N];
int ans;
void dfs( int x , int time , int tans ) {
	if(time < 0)
		return;
	if(x == n+1) {
		ans = max(ans,tans);
		return;
	}
	dfs(x+1,time,tans);
	dfs(x+1,time-T[x],tans+W[x]);
}
int main() {
	cin >> t >> n;
	for(int i = 1; i <= n; i++)
		cin >> T[i] >> W[i];
	dfs(1,t,0);
	cout << ans << endl;
	return 0;
}

以及冰冷的30分
开始第二步,将这个DFS改成"无需外部变量"的DFS
引入问题:如何记录答案?
答:可以将DFS引入返回值
于是我们得到了:

#include<bits/stdc++.h>
using namespace std; 
#define N 105
int n,t;
int T[N],W[N];
int ans;
int dfs( int x , int time) {
	if(x == n+1) {
		return 0;
	}
    int dfs1 ,dfs2 = -1;
	dfs1 = dfs(x+1,time);
	if(time >= T[x]){
		dfs2 = dfs(x+1,time-T[x]) + W[x];
	}
	return max(dfs1,dfs2);
}
int main() {
	cin >> t >> n;
	for(int i = 1; i <= n; i++)
		cin >> T[i] >> W[i];
	cout<<dfs(1,t)<<endl;
	return 0;
}

别急着提交,因为我们只是去掉了"外部变量",却没有优化复杂度
接下来,我们添加记忆化数组
多测试几组样例发现,其实对于相同的x和time,DFS的返回值总是相同的(废话)
接下来引入记忆化数组"mem",用它来记录下DFS每一个返回值,每次DFS判断一下mem是否有值,若有值,直接返回mem;若无值,继续搜索(类似于剪枝)
于是我们得到了:

#include<bits/stdc++.h>
using namespace std; 
#define N 105
int n,t;
int T[N],W[N];
int mem[N][10*N];
int ans;
int dfs( int x , int time) {
	if(mem[x][time] != 0){
		return mem[x][time];
	}
	if(x == n+1) {
		return 0;
	}
    int dfs1 ,dfs2 = -1;
	dfs1 = dfs(x+1,time);
	if(time >= T[x]){
		dfs2 = dfs(x+1,time-T[x]) + W[x];
	}
	mem[x][time] = max(dfs1,dfs2);
	return mem[x][time];
}
int main() {
	cin >> t >> n;
	for(int i = 1; i <= n; i++)
		cin >> T[i] >> W[i];
	cout<<dfs(1,t)<<endl;
	return 0;
}

注意:mem数组千万不要开小了(作者亲自踩坑)

鸣谢

参考:https://www.luogu.com.cn/article/qay8mori

posted @   薛儒浩  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· 深度对比:PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
点击右上角即可分享
微信分享提示