走迷宫
题目描述
现在你正在进行一个迷宫探险游戏。迷宫由一个n行m列的方阵构成,有一些格子是可以走的平地(用.表示),另外一些格子是不能走的墙壁(用#表示)。迷宫的最外侧一圈保证是墙壁。游戏起始时,你处于一块平地之上。游戏的目标是移动到一个给定的终点(保证是平地)。
每次移动可以向上下左右四个方向中的任意一个方向进行,但是每次选定好移动方向之后,必须沿着该方向一直移动,直至遇到墙壁不能继续前进为止。在遇到墙壁之前,中途不能停止移动。
你希望能够通过最少次数的移动从起点抵达终点。该游戏中,【一次移动】定义为【向着一个方向一直前进直至遇到墙壁停止】;【抵达终点】定义为【停留在终点】,即在移动过程中经过终点是不被算作抵达终点的,只有在遇到墙壁停止时正好停留在终点才被认为是抵达终点。
给定上述迷宫和起点、终点的位置,请输出从起点到终点所需要的最少的移动次数。如果无法抵达终点,则输出-1。
关于输入
输入的第一行包括两个正整数n和m,用空格分隔,表示迷宫矩阵的行数和列数。
之后n行,每行m个字符,用来描述整个迷宫,其中:
.表示平地
#表示墙壁
S表示起点,E表示终点。起点和终点可默认为是平地。
数据保证1<=n, m<=15,起点和终点位置不同,且迷宫的最外一圈一定是墙壁。
关于输出
输出一个整数,表示从起点抵达终点的最少移动次数。如果无法抵达终点,则输出-1。
例子输入
7 7 ####### #..S..# #.....# #.....# #.....# #....E# #######
例子输出
2
解题分析
起初看到此题,我尝试用DFS也即深度优先搜索去做,后来发现这是一个比较愚笨的方法,为何?因为这首先是要找到一个最短路径,用DFS容易造成过多的搜索,效率比较低。其次,这个走迷宫的方式有些特殊,也就是常见的折线路径,一折代表一次路径,用dfs的话需要采用一些其他的方式。不过,其实用DFS还是可以做的,这里就留给有兴趣的读者了。
于是我选择用BFS,
要解决这个问题,使用广度优先搜索(BFS)算法。这是因为BFS可以在未加权的图中找到从起点到终点的最短路径,即本问题中所需要的最少移动次数。每一次移动到墙壁停止可以视为图中的一步。
以下是解决这个问题的大致步骤:
-
初始化:创建一个队列用于存储在探索过程中达到的位置以及从起点到该位置所需的移动次数。同时,创建一个二维数组用作访问标记,以防止重复访问同一位置。
-
将起点加入队列:起点的位置和从起点到起点的移动次数(0)加入队列。
-
BFS遍历:
- 当队列不为空时,从队列中取出一个元素(位置和到达这个位置的移动次数)。
- 对于这个位置,尝试向上、下、左、右四个方向移动。
- 对每个方向,计算出从当前位置直到遇到墙壁为止的路径,并检查这个路径上是否可以到达终点。
- 如果在移动过程中可以到达终点,则立即返回当前移动次数加一。
- 如果移动方向是有效的(在迷宫内且没有被访问过),则将新位置(遇到墙壁的前一个位置)和移动次数加一加入队列。
- 标记当前位置为已访问。
-
检查结果:如果遍历完所有可能的路径后仍未找到终点,则返回-1。
代码实现
#include <iostream>
using namespace std;
bool maze[200][200],visited[200][200];
int x1,y1,x2,y2,n,m;
int top=0,bottom=0;
int dx[]={-1,1,0,0};
int dy[]={0,0,-1,1};
int ans=1e9;
class node{
public:
int x,y,step;
node(int i=-1,int j=-1,int s=-1){
x=i;
y=j;
step=s;
}
};
node queue[10005];
void push(node t){
queue[bottom++]=t;
}
void pop(){
top++;
}
node front(){
return queue[top];
}
bool empty(){
return top>=bottom;
}
node valid(node t,int i){
node tmp(-1,-1,-1);
while(t.x>=0 && t.x<n && t.y>=0 && t.y<m && maze[t.x][t.y]==0){
t.x+=dx[i];
t.y+=dy[i];
if(maze[t.x][t.y]==1 && visited[t.x-dx[i]][t.y-dy[i]]==0 && maze[t.x-dx[i]][t.y-dy[i]]==0){
tmp.x=t.x-dx[i];
tmp.y=t.y-dy[i];
tmp.step=0;
return tmp;
}
}
return tmp;
}
int main() {
scanf("%d%d",&n,&m);
char tmp;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
scanf(" %c",&tmp);
if(tmp=='#'){
maze[i][j]=1;
}
else if(tmp=='S'){
x1=i;
y1=j;
}
else if(tmp=='E'){
x2=i;
y2=j;
}
}
node start(x1,y1,0);
push(start);
node t;
node t1;
while(!empty()){
t=front();
pop();
if(visited[t.x][t.y]==1){
continue;
}
visited[t.x][t.y]=1;
for(int i=0;i<4;i++){
t1=valid(t,i);
if(t1.x!=-1){
t1.step=t.step+1;
if(t1.x==x2 && t1.y==y2){
ans=min(t1.step,ans);
}
push(t1);
}
}
}
if(ans!=1e9){
cout<<ans<<endl;
}
else{
cout<<-1<<endl;
}
return 0;
}