bfs提高练习题

P1363 幻象迷宫

幻象迷宫可以认为是无限大的,不过它由若干个N*M的矩阵重复组成。矩阵中有的地方是道路,用’.‘表示;有的地方是墙,用’#‘表示。LHX和WD所在的位置用’S’表示。也就是对于迷宫中的一个点(x,y),如果(x mod n,y mod m)是’.‘或者’S’,那么这个地方是道路;如果(x mod n,y mod m)是’#’,那么这个地方是墙。LHX和WD可以向上下左右四个方向移动,当然不能移动到墙上。

请你告诉LHX和WD,它们能否走出幻象迷宫(如果它们能走到距离起点无限远处,就认为能走出去)。如果不能的话,LHX就只好启动城堡的毁灭程序了……当然不到万不得已,他不想这么做。

输入格式
输入包含多组数据,以EOF结尾。

每组数据的第一行是两个整数N、M。

接下来是一个N*M的字符矩阵,表示迷宫里(0,0)到(n-1,m-1)这个矩阵单元。

输出格式
对于每组数据,输出一个字符串,Yes或者No。

输入输出样例
输入
5 4
##.#
##S#
#…#
#.##
#…#
5 4
##.#
##S#
#…#
…#.
#.##
输出
Yes
No


问题分析

审题要清楚,比如这个题审题不清导致做不出来。对于我这样语文不好的来说太难了。
幻象迷宫可以认为是无限大的,不过它由若干个N*M的矩阵重复组成。很重要的一句话。说多了都是泪😭。

回到正题,我们来看从‘S’点走,因为迷宫是无限大,所以从一个N*M的小迷宫走出去后(其实就是下标越界),如果进入另一个小迷宫,则是可以走出迷宫,因为迷宫是由若干个相同小迷宫组成的,可以按照第一次走出小迷宫的方式一直走下去。

思路有了,问题是如何实现呢?
当下标越界时,该如何处理,因为是无限大,总不能模拟整个迷宫吧。这是我们引入hash(散列函数)的散列表原理来表示。使用映射的方法。在小迷宫中进行操作,这时允许下标越界,但操作的数仍在小迷宫中进行。具体实现如下:
通过取模可以达到进入相邻的小迷宫里。

//对一个数取模,得到的结果:0 <= 取模运算式 < mod。
int tx = (nx + n*4)%n,ty = (ny + m*4)%m;   //将其放大4倍。防止为负值。

book数组不仅标记着是否走过,赋给他的值不同是标记不同小迷宫。因为我们是用容纳一个小迷宫的book数组来标记多个小迷宫,所以使用了散列表的性质。
如果book走过并且数值与t不相同并且不是墙,则说明走的为两个迷宫,故是可通的。

if (book[tx][ty] != t) return 1; 

book数组没标记按迷宫方法处理。

if (!book[tx][ty]) .....

代码如下:

#include <iostream>
#include <cstdio>//scanf、printf
#include <queue>
#include <algorithm>//pair
#include <cstring> //memset
using namespace std;

typedef pair<int,int> P;
const int MAX_N = 1505;
const int MAX_M = 1505; 

char maze[MAX_N][MAX_M];
int book[MAX_N][MAX_M];
int n,m;
int sx,sy;
int dx[4] = {1,-1,0,0},dy[4] = {0,0,1,-1};

//这里写一个hash函数。
int f(int x,int y){
	return (x+n*4)/n*520 + (y+m*4)/m*52;//这里要保证z = f(x,y)为映射关系即可。这里前后乘的数不可以相等。
}

bool bfs(){
	queue<P> que;
	que.push(P(sx,sy));
	book[sx][sy] = f(sx,sy);
	while (que.size())
	{
		P p = que.front();
		que.pop();
		for (int i = 0;i < 4;i ++)
		{
			int nx = dx[i] + p.first,ny = dy[i] + p.second;
			int tx = (nx + n*4)%n, ty = (ny + m*4)%m;//因为nx、ny可能为负数,取模时,应该加上mod的倍数。防止取模错误。
			int t = f(nx,ny);
			if (maze[tx][ty] == '#') continue;
			if (!book[tx][ty]) book[tx][ty] = t,que.push(P(nx,ny));
			else if (book[tx][ty] != t) return 1;
		}
	}
	return 0;
}



int main()
{
	while (scanf("%d%d",&n,&m) != EOF){
		memset(book,0,sizeof(book));//别忘了每次搜索标记数组都要更新一遍。
		for (int i = 0;i < n;i ++)
			scanf("%s",maze[i]);
		for (int i = 0;i < n;i ++){
			for (int j = 0;j < m;j ++){
				if (maze[i][j] == 'S'){
					sx = i;
					sy = j;
					break;
				}
			}
		}
		if (bfs()){
			printf("Yes\n");
		} else {
			printf("No\n");
		}
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值