记忆化搜索(2)P1434 [SHOI2002]滑雪——记搜经典例题+动态规划可解

P1434 [SHOI2002]滑雪

记忆化搜索(2)P1434 [SHOI2002]滑雪——记搜经典例题+动态规划可解_第1张图片

输入输出样例
输入 #1复制
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
输出 #1复制
25

总结目录

1 本题记忆化搜索思路,记忆化搜索与普通搜索的不同
2 总结记忆化搜索的基本框架
3 写本题之前的我的预期框架

1 记忆化搜索思路

https://www.luogu.org/problemnew/solution/P1434 中 路人七 的讲解是最清晰的。

本题直接使用dfs显然是会爆的,因为很多重复的点被反复的搜索过了。因此我们需要利用 已经搜索过的点 来计算。这个其实有点类似于动态规划的问题。在dfs的记忆化搜索中,搜索一个点的返回终止条件,除了该点 “已经走到尽头” ,还应该包括 “该点已经搜索过了”,也就是说,记忆化搜索都应该添加一个条件,即查表,看看当前结点是否已经被搜索过了,如果没有被搜索过,那么就去搜索它。

但是这对于题目本身也有一个要求,就是一次搜索就能得到一个最佳的值,后续的搜索不会对这个最佳的值产生影响。 在本题中,如果当前点没有被搜索过,那么我们先将当前点的步数置1,然后去搜索周围的点,如果可以搜到周围的点,那么周围的点的步数再+1可以和当前点比较,并更新当前点。

也就是说周围点的步数再加上一步,就可以得到当前点的步数。

2 记忆化搜索基本框架

1 返回条件:已经搜索过+搜到尽头
2 记忆矩阵 对应点值 进行初始化
3 搜索要有返回值,返回值直接从记忆矩阵里面读取

//dfs框架
int dfs(int row,int col){
	if(满足走到尽头的条件){
	return;
	}

	for(遍历周围的点){
		if(满足深搜的条件){
			dfs(更深一层)
		}
	}
	return....;
}

//记忆化搜索框架
int mark[maxsize][maxsize];
int dfs(int row,int col){
	if(mark[row][col]){
		//如果搜索的点已经被填充过;
		return;//直接返回
	}
	
	int res;//初始化一个值,也可以直接将mark[row][col]初始化为一个值,然后对mark[row][col]进行更新
	for(遍历周围的点){
		if(满足深搜的条件){
			int val=dfs(更深一层);
			res=  res.....val....;//根据题目关系,对res和更深一层搜索结果的返回值做一个处理,更新res
		}
	}
	
	//搜索完之后一定要将结果记录在mark[][]上!
	mark[row][col]=res;
	return mark[row][col];//最终返回结果为当前的mark[row][col]的值
}

3 写之前我的预期框架

暴力dfs。对每个点进行暴力dfs,结点状态为{row,col,step}。本题中由于梯度的帮助,不需要标记走过的路程。如果在其他题目中,可能需要标记走过的路程,并且使用回溯,不要在搜到尽头后清空! 在走到尽头的时候,将max值进行更新,取更大的一个即可。

代码

#include
#include
#include
#include
using namespace std;
#define maxsize 105
int mat[maxsize][maxsize];
int dir[][2] = {
	{-1,0},
	{1,0},
	{0,-1},
	{0,1}
};
//剪枝思路,我们只选择哪些局部最大的点进行dfs
int mark[maxsize][maxsize];
int res = 0;
int r, c;


int dfs2(int row, int col) {
	//对[row,col]点进行深搜,得到一个返回值。用mark来记录是否已经求得值
	if (mark[row][col] != 0) {
		return mark[row][col];//如果已经求过一次,直接使用记忆化搜索的值
	}
	mark[row][col] = 1;//当前格子也算,因此初始化为1
	for (int i = 0; i < 4; i++) {
		int newrow = row + dir[i][0];
		int newcol = col + dir[i][1];
		if (newrow <= r&&newrow >= 1 && newcol <= c&&newcol >= 1&&mat[row][col]>mat[newrow][newcol]) {//不越界且满足梯度的情况下
			int val=dfs2(newrow, newcol);
			mark[row][col] = max(mark[row][col], mark[newrow][newcol] + 1);
		}
	}

	return mark[row][col];
}

int main() {
	cin >> r >> c;
	fill(mat[0], mat[0] + maxsize*maxsize, INT_MAX);
	for (int row = 1; row <= r; row++) {
		for (int col = 1; col <= c; col++) {
			cin >> mat[row][col];
		}
	}
	
	for (int row = 1; row <= r; row++) {
		for (int col = 1; col <= c; col++) {
			res=max(res,dfs2(row, col));
		}
	}

	cout << res;
	return 0;
}

取巧代码(90%)

这个代码其实不能算记忆化搜索,应该算是一个基于预处理的dfs搜索。思路是先遍历整个mat,找到一些值得去深搜dfs的点,然后再去进行dfs。具体的值得深搜的含义是:这个点是一个局部最大值,也就是说它的周围的点都比它小。

因此我们先遍历整个mat[i][j],判断(i,j),比较这个点和周围4个方向的点的值,是否是局部最大,不是就置0,是就置1.这样我们最终搜索的时候只搜索置1的点,即局部最大点。这个代码得到了90%的分数。

#include
#include
#include
#include
using namespace std;
#define maxsize 105
int mat[maxsize][maxsize];
int dir[][2] = {
	{-1,0},
	{1,0},
	{0,-1},
	{0,1}
};
//剪枝思路,我们只选择哪些局部最大的点进行dfs
int mark[maxsize][maxsize];
int res = 0;
int r, c;

void PreProcess(int r, int c) {
	//建立预处理矩阵,找到局部最大的点
	for (int row = 1; row <= r; row++) {
		for (int col = 1; col <= c; col++) {
			int flag = 1;
			for (int i = 0; i < 4; i++) {
				int newrow = row + dir[i][0];
				int newcol = col + dir[i][1];
				if (newrow > r || newrow<1 || newcol>c || newcol < 1) {
					continue;//如果越界了,就不讨论这个情景
				}
				if (mat[row][col] < mat[newrow][newcol]) {
					flag = 0;//如果没有越界,判断是否比周围小,小的话就没有dfs的必要了
					break;
				}
			}
			mark[row][col] = flag;
		}
	}
}

void dfs(int row, int col,int step) {
	//对[row,col]点进行深搜,得到它的最大长度,并比较
	for (int i = 0; i < 4; i++) {
		int newrow = row + dir[i][0];
		int newcol = col + dir[i][1];
		if (newrow > r || newrow<1 || newcol>c || newcol < 1) {
			continue;//如果越界了,就不讨论这个情景
		}
		if (mat[row][col] > mat[newrow][newcol]) {
			dfs(newrow, newcol, step + 1);//更小的话可以深搜
		}
	}
	res = max(res, step);//到底结尾之后无法深搜了,就直接比较结果
}

int main() {
	cin >> r >> c;
	fill(mat[0], mat[0] + maxsize*maxsize, INT_MAX);
	for (int row = 1; row <= r; row++) {
		for (int col = 1; col <= c; col++) {
			cin >> mat[row][col];
		}
	}
	PreProcess(r, c);
	for (int row = 1; row <= r; row++) {
		for (int col = 1; col <= c; col++) {
			if (mark[row][col] == 1) {
				dfs(row, col, 1);
			}
		}
	}
	cout << res;
	return 0;
}

你可能感兴趣的:(记忆化搜索)