HDU 1010 迷宫 BFS+DFS做法 不建议使用BFS的原因

	一只小狗在一个古老的迷宫里发现了一块骨头,它太喜欢了。当它拿起来这个骨头的时候,迷宫开始坍塌。小狗意识到这是个陷阱,它拼了命想逃离。

	迷宫有n行m列,其中X代表墙,小狗不能走入。S表示小狗起始位置,D代表出口,.表示空地。

	小狗能往上下左右四个方向走,迷宫中有一道出口门,只在第T秒时开启,持续一秒。问小狗能否逃离。

	小狗原位置到达新位置需要1秒,且原位置会坍塌(小狗不能往回走)

		1<N,M<7,T<50
1000ms32768KB

BFS做法(空间超限)

这道题真的不建议用BFS来做

一般BFS要改原图,然后把新状态(x坐标,y坐标,经过时间)加入队列,但是下一个结点出来的时候(注意,BFS广搜,相邻的两个状态结点是并列关系,他们是从同一个状态开的多分支,也就是平行宇宙),还是会参照原图,所以原图就一直在被更新,而没有回溯。意思就是假如现在队列出队的是1,然后对于状态结点1又有四个方向可以入队,我把1所在的位置标记为X,1处理完了,然后出队的是2,状态2是从状态0过来的,但是由于先出队的1修改了原图,所以状态2参照的原图是不准确的,可以说是时空交错了。状态2的时间状态对应了错误的空间状态。
所以为了避免队列前面的结点修改原图对后面的结点造成影响,就不能只有一个原图,所以对于每个状态结点都保存自己的空间状态,这样每次出队结点都拿自己的空间状态来做处理,就不会有错,但是这样的空间花费是巨大的
在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define DEBUG
using namespace std;

struct node{
	int x, y, t,state;
	node() {};
	node(int x, int y, int t,int state){
		this->x = x;
		this->y = y;
		this->t = t;
		this->state = state;
	};
};

int dir[4][2] =
{
	{0,1},
	{0,-1},
	{1,0},
	{-1,0}
};

char g[250000][7][7];
int n, m, t;
int sx, sy, ex, ey;
//int maxsize;
char temp;
int all_state;


bool bfs(int sx,int sy) {

	queue<node> q;
	q.push(node(sx, sy, 0, all_state));
	node now;
	int x, y, nx, ny;
	while (q.size()) {
						#ifdef DEBUG
							//maxsize = max(maxsize, (int)q.size());
						#endif // DEBUG
		now = q.front();
		q.pop();
						#ifdef DEBUG
							printf("x=%d y=%d t=%d state=%d\n", now.x, now.y, now.t,now.state);
							printf("\n");
							for (int i = 0; i < n; i++) {
								printf("%s\n", g[now.state][i]);
							}
							printf("\n");
						#endif // DEBUG
		x = now.x;
		y = now.y;
		if (now.t == t) {
			if (x == ex && y == ey) {
																#ifdef DEBUG
																for (int i = 0; i < n; i++) {
																	printf("%s\n", g[now.state][i]);
																}
																#endif // DEBUG
				return true;
			}
			continue;
		}
		if(g[now.state][x][y]!='S') g[now.state][x][y] = '*';
		for (int i = 0; i < 4; i++) {
			nx = x + dir[i][0];
			ny = y + dir[i][1];
			temp = '0';
			if (nx >= 0 && nx < n && ny >= 0 && ny < m && g[now.state][nx][ny] != 'X'&&g[now.state][nx][ny] != 'S' && g[now.state][nx][ny] != '*') {
				swap(temp, g[now.state][nx][ny]);
				memcpy(g[++all_state], g[now.state], sizeof g[now.state]);
				q.push(node(nx, ny, now.t + 1,all_state));
				swap(temp, g[now.state][nx][ny]);
			}
		}
	}
	return false;
}

int main() {
	//printf("struct size:%d\n", sizeof node);
	while (scanf("%d %d %d", &n, &m, &t), (n&&m&&t)) {
		//maxsize = 0;
		memset(g, 0, sizeof g);
		all_state = 0;
		for (int i = 0; i < n; i++) {
			scanf("%s", g[all_state] + i);
			for (int j = 0; j < m; j++) {
				if (g[all_state][i][j] == 'S') {
					sx = i;
					sy = j;
				}
				if (g[all_state][i][j] == 'D') {
					ex = i;
					ey = j;
				}
			}
		}
		if ((ex + ey + sx + sy + t) % 2 != 0) {
			printf("NO\n");
			continue;
		}
		if (bfs(sx, sy)) {
			printf("YES\n");
		}
		else {
			printf("NO\n");
		}
		//printf("maxsize:%d\n", maxsize);
	}
	return 0;
}
运行样例
6 6 12
S.X...
..X..X
..XX..
......
......
.....D

可以看到队列的容量达到了1673
在这里插入图片描述

运行样例
6 6 27
S.X..D
..X..X
..XX..
......
..X...
......

队列达到了2522
在这里插入图片描述
我觉得我的算法是没有问题的,就是内存超了
经过OJ的多次打击之后,我还是走正道了

DFS做法

思路就是从起始点走呗,从这个点走到下个点,我就把这个点变成X,为了防止往回走,然后又是四个方向走下去,如果时间超了,就说明这种走法不行,得回到上一个点换个方向走,如果上一个点的所有方向都走完了还是不行,就得回溯改,把X换回 . ,给以后的走法留条路。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
//#define DEBUG
using namespace std;

int n, m, t;
char g[7][7];
int sx, sy, ex, ey;

int dir[4][2] = {
	{0,1},
	{0,-1},
	{1,0},
	{-1,0}
};

bool ok;

bool check(int x, int y) {
	return x >= 0 && x < n&&y >= 0 && y < m&&g[x][y] != 'X';
}

void dfs(int x, int y, int time) {
#ifdef DEBUG
	printf("x=%d y=%d time=%d\n", x, y, time);
	for (int i = 0; i < n; i++) {
		puts(g[i]);
	}
	printf("\n");
#endif // DEBUG

	if (time == t) {
		if (x == ex && y == ey) {
			ok = true;
		}
		return;
	}

	int nx, ny;
	g[x][y] = 'X';
	for (int i = 0; i < 4; i++) {
		nx = x + dir[i][0];
		ny = y + dir[i][1];
		if (check(nx, ny)) {
			
			dfs(nx, ny,time+1);
			if (ok)return;
		}
	}
	g[x][y] = '.';
}

int main() {
	while (scanf("%d %d %d", &n, &m, &t), (n&&m&&t)) {
		ok = false;
		memset(g, 0, sizeof g);
		for (int i = 0; i < n; i++) {
			scanf("%s", g + i);
			for (int j = 0; j < m; j++) {
				if (g[i][j] == 'S') {
					sx = i;
					sy = j;
				}
				if (g[i][j] == 'D') {
					ex = i;
					ey = j;
				}
			}
		}
		if ((ex + ey + sx + sy + t) % 2 != 0) {
			printf("NO\n");
			continue;
		}
		dfs(sx, sy,0);
		if (ok) {
			printf("YES\n");
		}
		else {
			printf("NO\n");
		}

	}
	return 0;
}
/*
6 6 27
S.X..D
..X..X
..XX..
......
..X...
......
*/

有些时候是这么回事,求最优解的问题,比如迷宫最短路,最少操作几次,最少耗费时间,我们本能反应就是BFS,而且这类题型往往数据量还比较大,用DFS的话要遍历整个解空间树求个最小值,就会TLE,而这道题目我们也本能地以为可以用BFS做,但是这道题有个特殊的条件是你不能往回走,BFS怎么回溯啊,拷贝地图作为每一种情况,一一对应吗?那么就会MLE,用DFS反而更快。

总结

两种做法都用到了奇偶性剪枝,其实还有个剪枝:

  • 如果起始点坐标和终点坐标的曼哈顿距离都小于t,那么直接不行
谨以此篇,纪念此题
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值