IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 信奥1215:迷宫(详解标记数组的还原) -> 正文阅读

[C++知识库]信奥1215:迷宫(详解标记数组的还原)


1215:迷宫
时间限制: 1000 ms 内存限制: 65536 KB

【题目描述】
一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n×n
的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能办到。如果起点或者终点有一个不能通行(为#),则看成无法办到。

【输入】
第1行是测试数据的组数k,后面跟着k组输入。每组测试数据的第1行是一个正整数n(1≤n≤100),表示迷宫的规模是n×n的。接下来是一个n×n的矩阵,矩阵中的元素为.或者#。再接下来一行是4个整数ha,la,hb,lb,描述A处在第ha行, 第la列,B处在第hb行, 第lb列。注意到ha,la,hb,lb全部是从0开始计数的。

【输出】
k行,每行输出对应一个输入。能办到则输出“YES”,否则输出“NO”。

最初的写法(超时了)

刚开始没有多想,就用dfs去做了,代码如下:

#include<bits/stdc++.h>
using namespace std;

int k, n;
char a[105][105]; //地图 
int b[105][105]; //标记数组 
int d[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; //方向数组 
int startx, starty, endx, endy;
bool flag;

void dfs(int x, int y) {
	//递归出口
	if (x == endx && y == endy) {
		cout << "YES" << endl;
		flag = true;
		return;
	}
	
	//递归条件
	for (int i = 0; i < 4; i++) {
		int nextx = x + d[i][0];
		int nexty = y + d[i][1];
		if (nextx >= 0 && nexty >= 0 && nextx < n && nexty < n && a[nextx][nexty] == '.' && !b[nextx][nexty]) {
			//没越界、没有障碍物、没走过,同时满足三者才可以走
			b[nextx][nexty] = 1;
			dfs(nextx, nexty);
			b[nextx][nexty] = 0; 
		}
	} 
}

int main()
{
	cin >> k;
	for (int i = 0; i < k; i++) {
		//初始化工作 
		memset(a, 0, sizeof(a)); 
		memset(b, 0, sizeof(b)); 
		
		//输入地图维度 
		cin >> n;
		
		//输入地图
		for (int row = 0; row < n; row++) {
			for (int col = 0; col < n; col++) {
				cin >> a[row][col];
			}
		} 
		
		//输入起点终点
		cin >> startx >> starty >> endx >> endy;
		
		//特判
		if (a[startx][starty] == '#' || a[endx][endy] == '#') { //起点或终点为# 
			cout << "NO" << endl;
			continue;
		} 
		
		flag = false;
		b[startx][starty] = 1;
		dfs(startx, starty);
		if (flag == false) cout << "NO" << endl;
	}
	return 0;
}

结果超时了…

超时写法原因总结

通过与大佬交流后发现,此题回溯时不需要还原标记数组。那么问题来了,为什么不用还原标记数组呢?

是的,最初接触深搜回溯算法时我们经常会写这样的代码:

//b为标记数组
b[x] = 1; //标记已经走过
dfs(); //深搜
b[x] = 0; //回溯并还原标记数组

但是我们要知道这种写法对应的题目要求一般是这样的:“寻找最短路径”、“求总方案数”、“列出所有方案”等。这种就要求我们穷尽所有情况。比如我们一条路走不通了,那么我们回去,而且在我们回去之后,我们以后还是可以经过这条路的,因为我们已经在回溯的过程中还原了标记数组,形象来说就是我们抹除了我们走过这里的记忆,所以以后有可能还会来这儿。


但是对于像本题这种题,我们一般这样写:

//b为标记数组
b[x] = 1; //标记已经走过
dfs(); //深搜

这种对应的题目一般是“是否能走到”、“有无一种方案能够到达”等。因为我们的目的是找到终点,所以不需要像上面那样穷尽所有的方案,我们只要找到了,那么就停止搜索了。比如我们一条路走不通了,那么我们回去,并且在回去的路上,我们用土把这些走过的地方填上,以后就不走这儿了(这条路找不到终点那我以后还走这儿干嘛!?)。直到我们找到终点,那么搜索就停止了。这样写之所以不会超时,是因为我们把那些走过的路都填上了 ,以后就不会再走了,降低了复杂度。

正确写法

其实就是把b[nextx][nexty] = 0;注释掉就行。

#include<bits/stdc++.h>
using namespace std;

int k, n;
char a[105][105]; //地图 
int b[105][105]; //标记数组 
int d[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; //方向数组 
int startx, starty, endx, endy;
bool flag;

void dfs(int x, int y) {
	//递归出口
	if (x == endx && y == endy) {
		cout << "YES" << endl;
		flag = true;
		return;
	}
	
	//递归条件
	for (int i = 0; i < 4; i++) {
		int nextx = x + d[i][0];
		int nexty = y + d[i][1];
		if (nextx >= 0 && nexty >= 0 && nextx < n && nexty < n && a[nextx][nexty] == '.' && !b[nextx][nexty]) {
			//没越界、没有障碍物、没走过,同时满足三者才可以走
			b[nextx][nexty] = 1;
			dfs(nextx, nexty);
			//b[nextx][nexty] = 0; 
		}
	} 
}

int main()
{
	cin >> k;
	for (int i = 0; i < k; i++) {
		//初始化工作 
		memset(a, 0, sizeof(a)); 
		memset(b, 0, sizeof(b)); 
		
		//输入地图维度 
		cin >> n;
		
		//输入地图
		for (int row = 0; row < n; row++) {
			for (int col = 0; col < n; col++) {
				cin >> a[row][col];
			}
		} 
		
		//输入起点终点
		cin >> startx >> starty >> endx >> endy;
		
		//特判
		if (a[startx][starty] == '#' || a[endx][endy] == '#') { //起点或终点为# 
			cout << "NO" << endl;
			continue;
		} 
		
		flag = false;
		b[startx][starty] = 1;
		dfs(startx, starty);
		if (flag == false) cout << "NO" << endl;
	}
	return 0;
}

相似练习题

可以看一下这道题,也是不需要还原标记数组的。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-09 20:30:33  更:2022-02-09 20:31:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:24:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码