[C++]洛谷 滑雪 dfs+记忆化搜索详解

本文介绍了如何利用记忆化搜索解决最长大递减子序列问题,通过避免重复搜索来优化时间复杂度。文章通过一个具体的样例分析了常规DFS方法的不足,并详细阐述了记忆化搜索的工作原理,包括搜索方向、路径更新和存储优化。最后,给出了C++实现的记忆化搜索代码,展示了其在处理大型矩阵时的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[原题]

[输入样例]

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

[输出样例]

25

[解题思路]

给出一个m*n的矩阵grid,每一格代表一个高度,要求我们找到一条路径,满足高度严格递减且长度最长。显然,对于这种问题,我们可以枚举起点,并通过dfs来得到最长的路径。

对于dfs的过程也很好理解:向上下左右四个方向搜索,若下一格的高度小于本格,则继续搜索且长度++,直到找出最长的路径。

但是,以题目给出的这个样例为例,假如我们使用上述方法会出现一个巨大的漏洞(此处以(i,j)表示grid[i][j]的单元格):

假如我们从(0,0)开始搜索,由于四周没有比1小的数,搜索直接结束。

接下来,我们从(0,1)开始搜素,搜索路径为(0,1)->(0,0),共调用两层递归。

接下来,我们从(0,2)开始搜索,搜索路径为(0,2)->(0,1)->(0,0),共调用三层递归。

............

不难发现,按照这种方式继续下去,仅(0,0)处就被搜索了数十次,这在时间上是一个非常巨大的损耗,倘若我们的grid数组非常大,那么由于重复搜索消耗的时间是难以估量的。

因此,我们需要采取一个手段来避免这种时间的损耗:记忆化搜索。简单来说,就是另外使用一个容器来存储各点的路径最大值。这样的话,仅需执行过一次对本格的搜索,下一次重复检索本格时就可以直接从对应的容器中取值了。

最后,我们直接上代码,结合本题来理解一下记忆化搜索的便利:

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

int n, m;//矩阵的长宽
int dx[4] = { 1,0,-1,0 }, dy[4] = { 0,1,0,-1 };//搜索的四个方向
vector<vector<int>> mem;//用于记忆化存储

//传入搜索起始位置的横纵坐标x,y及矩阵grid
int dfs(int x, int y, vector<vector<int>>& grid)
{
	//如果已被记忆化存储,则直接返回
	if (mem[x][y] != -1) return mem[x][y];

	int ret = 1;//无论如何都要经过本格,因此初值设为1

	//向四个方向搜索,求其路径最大值
	for (int i = 0; i < 4; i++)
	{
		//越界判断 && 后一格的高度小于前一格
		if (x + dx[i] >= 0 && x + dx[i] < n && y + dy[i] >= 0 && y + dy[i] < m && grid[x + dx[i]][y + dy[i]] < grid[x][y])
		{
			ret = max(ret, dfs(x + dx[i], y + dy[i], grid) + 1);//注意路径+1
		}
	}

	//更新mem数组
	mem[x][y] = ret;

	//返回ret
	return ret;
}

int main()
{
	//读取数据及初始化容器
	int ans = 0;
	cin >> n >> m;
	vector<vector<int>> grid(n, vector<int>(m));
	mem = vector<vector<int>>(n, vector<int>(m, -1));//初值为-1,表示未经过存储
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> grid[i][j];
		}
	}

	//以每一个位置作为搜索起始位置,进行dfs搜索
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			//取其最大值
			ans = max(ans, dfs(i, j, grid));
		}
	}

	//输出结果
	cout << ans;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值