本文包含对y总代码版本的详细阐释
走迷宫
给定一个 n×m的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,11表示不可通过的墙壁。
最初,有一个人位于左上角 (1,1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
请问,该人从左上角移动至右下角 (n,m)处,至少需要移动多少次。
数据保证 (1,1) 处和 (n,m)处的数字为 0,且一定至少存在一条通路。
输入格式
第一行包含两个整数 n和 m。
接下来 n 行,每行包含 m 个整数(0 或 1),表示完整的二维数组迷宫。
输出格式
输出一个整数,表示从左上角移动至右下角的最少移动次数。
数据范围
1≤n,m≤100
输入样例:
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
输出样例:
8
题解说明:
本题bfs的基本框架:设定一个队列,从图中的某一个点开始遍历,每一轮取出队头元素,通过偏移量对该点的四个方向的点进行遍历,判断其是否符合条件,直到队列为空
条件:
1.遍历到的点不超过图的边界
2.这个点应该是0(非墙壁)
3.这个点不能是已经标记走过的点
若符合条件,那么把这个点标记为已走过,并加入队列,经过bfs操作之后,把已求出的右下角的点的距离输出来。
那么为什么这样遍历所得出的距离就是最短路径呢?
因为bfs是从点的四周开始一层层扩散的,每一次遍历都会搜索该点四周距离最短的点,那么一旦搜索到了目标,目标点就会被标记为已搜索过,最短距离也将定下来,那么即使后面仍然可能被搜到,也因为已标记无法改变其距离。那么我们最后得出的距离也就是最短路径了。
代码如下:
#include <iostream>
#include <cstring>// 引入C字符串库,用于memset函数
using namespace std;
const int N = 110 ;
//注意最短路问题必须要所有边的权重都为1时才能采用bfs
typedef pair<int, int > PII;// 键值对,用于表示图中的节点 ,
int n, m;// 表示图的行数和列数
int g[N][N]; //存储图的邻接矩阵
int d[N][N]; //存的是每一个点到起点的距离
PII q[N * N]; // 定义一个数组q,用于存储待处理的节点
//pair类型的基本语法为pair<Type1, Type2> p,其中Type1和Type2分别表示键和值的数据类
//pair类型的对象有两个成员,分别名为first和second,可以使用普通的点操作符来访问其成员。
int bfs() //返回从起点到终点的最短距离
{
int hh = 0, tt = 0; //模拟队列用于存储路径上的点
q[0] = {0,0}; // 将起点加入队列,由于起点是左上角所以队列从(0,0)开始
memset(d, -1, sizeof d);//先把距离初始化为-1,表示没有走过
d[0][0] = 0; //表示已经走过了
//用向量表示某个点的四个方向
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; // 定义四个方向的偏移量
while(hh <= tt )//当队列不空
{
auto t = q[hh ++] ; //每一次取出队头元素 auto(自动类型推断)
for( int i = 0; i < 4; i ++ ){ // 对每一个方向进行遍历
int x = t.first + dx[i], y = t.second + dy[i];
//(x,y)表示沿着这个方向走会走到哪个点
if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1)
{
// 如果沿着这个方向走没有超过边界且该位置没有被访问过
d[x][y] = d[t.first][t.second] + 1; // 更新该点的最短距离
q[ ++ tt] = {x,y}; // 将该点加入队列中等待处理
}
}
}
return d[n-1][m-1]; //把右下角的点的距离输出来
}
int main()
{
cin >> n >> m;
for( int i = 0; i < n; i ++ )
{
for( int j = 0; j < m; j ++)
{
cin >> g[i][j];
}
}
cout << bfs() << endl;
return 0;
}