问题描述:给定一个大小为N*M的迷宫。迷宫有通道和墙壁组成,每一步可以向邻接的上下左右的通道移动。请求出从起点到终点所需要的最小步数。
例如,N为10, M为10,输入的迷宫如下表示,其中S表示起点,G表示终点,”.”表示通道,“#”表示墙壁
#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.###.
....#...G#
则输出为22。
解法:可以将每一步看作是一次状态转移,要求步数最少则是发生状态转移的次数最少。从每一点到下一个点最多共有4次状态转移,可以利用一个宽度优先搜索来进行实现。宽度优先搜索中发生状态转移次数少的点总是会首先被访问,当我们第一次访问到该点时所经历的状态转移次数即为起点到该点的最短距离,这样我们可以利用一个数组d[N][M]来保存已经访问过的点,这样就可以由近及远的搜索。在这里求最短的距离,我们不妨用这个数组将最短距离保存起来,用一个无穷大INF来初始化这个数组,当该点没用被访问过时,相对应的d数组中的值就应该为INF,当我们第一次访问到这个点时对d数组进行更新,存储下到该点总共的状态转次数,即为起点到该点的最短距离。搜索结束后我们返回终点位置所对应的d数组中的值,如果仍为INF则表示不存在起点到终点的路径。
在下面的程序中,我们使用一个pair<int, int>类型来表示一个点的坐标,使用两个数组大小dx[4],dy[4]来表示4个方向向量,这样通过一个循环就可以遍历四个方向的移动。
#include<iostream>
#include<queue>
using namespace std;
const int INF = 1000000;
typedef pair<int, int> P;
char mi[10][11] = {
"#S######.#",
"......#..#",
".#.##.##.#",
".#........",
"##.##.####",
"....#....#",
".#######.#",
"....#.....",
".####.###.",
"....#...G#"
};//迷宫数组
int N = 10, M = 10;//迷宫大小
int sx = 0, sy = 1;
int gx = 9, gy = 8;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int d[10][10];
int bfs(){
//初始化d数组
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
d[i][j] = INF;
}
}
queue<P> que;
//取出起点
que.push(P(sx, sy));
d[sx][sy] = 0;
//进行宽度优先搜索
while(que.size()){
P p = que.front();
que.pop();
if (p.first == gx && p.second == gy)//已经搜索到终点
{
break;
}
for (int i = 0; i < 4; i++)
{
int nx = p.first + dx[i];
int ny = p.second + dy[i];
//d[nx][ny]==INF即表示该点未被访问过
if (nx>=0 && nx<N && ny>=0 && ny<M && mi[nx][ny]!='#' && d[nx][ny]==INF)
{
d[nx][ny] = d[p.first][p.second] + 1;
que.push(P(nx, ny));
}
}
}
return d[gx][gy];
}
int main(){
int r = bfs();
if (r == INF)
{
printf("不存在路径!\n");
}
else
{
printf("%d\n", r);
}
system("pause");
return 0;
}