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;
}