题目描述
给定一个N×M 的网格迷宫G。G 的每个格子要么是道路,要么是障碍物(道路用 1 表示,障碍物用 0 表示)。
已知迷宫的入口位置为(x1,y1),出口位置为(x2,y2)。问从入口走到出口,最少要走多少个格子。
输入描述
输入第 1 行包含两个正整数N,M,分别表示迷宫的大小。
接下来输入一个N×M 的矩阵。若 Gi,j=1 表示其为道路,否则表示其为障碍物。
最后一行输入四个整数x1,y1,x2,y2,表示入口的位置和出口的位置。
1≤N,M≤,0≤Gi,j≤1,1≤x1,x2≤N,1≤y1,y2≤M。
输出描述
输出仅一行,包含一个整数表示答案。
若无法从入口到出口,则输出−1。
输入输出样例
示例 1
输入
5 5
1 0 1 1 0
1 1 0 1 1
0 1 0 1 1
1 1 1 1 1
1 0 0 0 1
1 1 5 5
输出
8
运行限制
语言 | 最大运行时间 | 最大运行内存 |
---|---|---|
C++ | 1s | 128M |
C | 1s | 128M |
Python3 | 1s | 128M |
Java | 1s | 128M |
思路
读题可知要求寻找两点的最短路径,可以用宽搜的办法。首先输入了一系列数值,将矩阵保存在二维数组,注意题目给的坐标是正常类型(可转成从0开始的坐标,即输入后减1),随后进行宽搜。简单来说,宽搜是从一点出发,向四周扩散寻找路,期间可能遇见越界、障碍物等,并且在扩散的过程中记录新点距离起点的距离,直到找到终点。bfs的具体思路为:
1. 首先,我们创建一个队列 (先进先出)来存储待访问的位置。初始时,我们将起点(x1,y1)加入队列。
2. 然后我们进入一个循环,当队列非空时,执行一下操作:
2.1 从队列的头部取出一个位置
,这就是我们当前要处理的位置。(t.x, t.y)
2.2 然后我们尝试向四个方向移动(顺时针),对于每个方向,计算出新的位置(tx, ty)。
2.3 接下来,我们需要检查新的位置是否在网格内(越界),以及是否可以走(障碍)。如果新的位置在网格内,并且可以走,那么继续处理这个位置。
2.4 将新的位置(tx, ty)标记为 ,表示这个位置已经被访问过,不需要再访问了。然后,我们计算到这个位置的距离,它等于从起点到当前位置的距离加一。
2.5 如果新的位置就是终点 (x1, y2),那么就找到了一条路径,可以直接返回这个距离,这是由于BFS是按照距离递增的顺序来访问位置的,所以当我们第一次访问到终点时,找到的就是最短路径。
2.6 最后我们将新的位置(tx, ty)加入队列,等待后续处理。
3. 当队列为空时,如果我们还没有找到路径,那就表示从起点到终点没有联通路径,返回-1。
代码一
#include <iostream>
#include <queue>
using namespace std;
//定义集合键值对
#define x first
#define y second
const int N = 1e3 + 10;
typedef pair<int, int> PII;
int n, m, A, B, C, D;
char g[N][N];
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int count = 0;
int bfs(){
queue<PII> q; //初始化坐标型队列
q.push({A, B}); //将起点放入队列
while(!q.empty()){ //当队列不为空,进入循环
auto t = q.front(); //获取当前位置
q.pop(); //将其从队列移出
for(int i = 0; i < 4; i++){
int tx = t.x + dx[i], ty = t.y + dy[i]; //获取新的位置
if(tx < 0 || ty < 0 || tx >=n || ty >=m || g[tx][ty]=='0') //越界、障碍
continue;
g[tx][ty] = '0'; //可访问设为已访问
count++;
if(tx == C && ty == D)
return count;
q.push({tx, ty}); //没到终点,将新的位置放入队列,等待下一次调用
}
}
return -1; //无路径
}
int main(){
cin >> n >> m;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
cin >>g[i][j];
cin >> A >> B >> C >> D;
//从0开始计算坐标
A--;
B--;
C--;
D--;
cout << bfs();
return 0;
}
代码执行结果
上述代码一是有错误的,即利用了count++的方式来计算路径,但这只适用于每一次四个方向的寻找都只有一条路的问题,反之则将其余可走路径也进行了count++。
代码二
#include <iostream>
#include <queue>
using namespace std;
//定义集合键值对
#define x first
#define y second
const int N = 1e3 + 10;
typedef pair<int, int> PII;
int n, m, A, B, C, D;
char g[N][N];
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int dist[N][N];
int bfs(){
queue<PII> q; //初始化坐标型队列
q.push({A, B}); //将起点放入队列
while(!q.empty()){ //当队列不为空,进入循环
auto t = q.front(); //获取当前位置
q.pop(); //将其从队列移出
for(int i = 0; i < 4; i++){
int tx = t.x + dx[i], ty = t.y + dy[i]; //获取新的位置
if(tx < 0 || ty < 0 || tx >=n || ty >=m || g[tx][ty]=='0') //越界、障碍
continue;
g[tx][ty] = '0'; //可访问设为已访问
dist[tx][ty] = dist[t.x][t.y] + 1; //距离加1(初始为0)
if(tx == C && ty == D)
return dist[tx][ty];
q.push({tx, ty}); //没到终点,将新的位置放入队列,等待下一次调用
}
}
return -1; //无路径
}
int main(){
cin >> n >> m;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
cin >>g[i][j];
cin >> A >> B >> C >> D;
//从0开始计算坐标
A--;
B--;
C--;
D--;
cout << bfs();
return 0;
}
代码执行结果
上述代码二初始化一个数组来存放每一个坐标距离起点的距离,保证每个坐标的距离是独立的。
总结
宽搜的前置知识是坐标系和队列,前者用来存储矩阵数据,后者用来对数据进行先进先出的操作。