|
|
|
|
##[$AcWing$ $844$. 走迷宫](https://www.acwing.com/problem/content/846/)
|
|
|
|
|
|
|
|
|
|
### 一、题目描述
|
|
|
|
|
给定一个 $n×m$ 的二维整数数组,用来表示一个迷宫,数组中只包含 $0$ 或 $1$,其中 $0$ 表示可以走的路,$1$ 表示不可通过的墙壁。
|
|
|
|
|
|
|
|
|
|
最初,有一个人位于左上角 $(1,1)$ 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
|
|
|
|
|
|
|
|
|
|
请问,该人从左上角移动至右下角 $(n,m)$ 处,至少需要移动多少次。
|
|
|
|
|
|
|
|
|
|
数据保证 $(1,1)$ 处和 $(n,m)$ 处的数字为 $0$,且一定至少存在一条通路。
|
|
|
|
|
|
|
|
|
|
**输入格式**
|
|
|
|
|
第一行包含两个整数 $n$ 和 $m$。
|
|
|
|
|
|
|
|
|
|
接下来 $n$ 行,每行包含 $m$ 个整数($0$ 或 $1$),表示完整的二维数组迷宫。
|
|
|
|
|
|
|
|
|
|
**输出格式**
|
|
|
|
|
输出一个整数,表示从左上角移动至右下角的最少移动次数。
|
|
|
|
|
|
|
|
|
|
**数据范围**
|
|
|
|
|
$1≤n,m≤100$
|
|
|
|
|
|
|
|
|
|
**输入样例:**
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**输出样例:**
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
8
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 二、理解与感悟
|
|
|
|
|
|
|
|
|
|
1. $bfs$适合寻找最短(最长)的路径,因为是按层一层层找的,第一个符合条件的就是最短的路径。
|
|
|
|
|
|
|
|
|
|
2. 走迷宫一般使用$dx$,$dy$,就是左上右下,或者上右下左的二组变量常数,在蛇形排列中,还强调了四个方向的初始化方向,在走迷宫时,不强调顺序,哪个方向先来都是一样的。
|
|
|
|
|
|
|
|
|
|
3. 距离数组一般初始化为$-1$,表示未探索的位置。有时其它题中也表示不可以走的墙,具体问题再具体分析。
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
memset(d,-1,sizeof d);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 三、$bfs$解法
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
#define x first
|
|
|
|
|
#define y second
|
|
|
|
|
typedef pair<int, int> PII;
|
|
|
|
|
|
|
|
|
|
const int N = 110;
|
|
|
|
|
int n, m;
|
|
|
|
|
int g[N][N];
|
|
|
|
|
int d[N][N];
|
|
|
|
|
|
|
|
|
|
int dx[] = {-1, 0, 1, 0};
|
|
|
|
|
int dy[] = {0, 1, 0, -1};
|
|
|
|
|
|
|
|
|
|
queue<PII> q;
|
|
|
|
|
int bfs() {
|
|
|
|
|
q.push({1, 1});
|
|
|
|
|
memset(d, -1, sizeof d);
|
|
|
|
|
d[1][1] = 0;
|
|
|
|
|
|
|
|
|
|
while (q.size()) {
|
|
|
|
|
if (~d[n][m]) break; // 不等于-1,表示已找到小哈,剪枝
|
|
|
|
|
auto u = q.front();
|
|
|
|
|
q.pop();
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
int tx = u.x + dx[i], ty = u.y + dy[i];
|
|
|
|
|
if (tx == 0 || tx > n || ty == 0 || ty > m) continue;
|
|
|
|
|
|
|
|
|
|
if (g[tx][ty] == 0 && d[tx][ty] == -1) {
|
|
|
|
|
d[tx][ty] = d[u.x][u.y] + 1;
|
|
|
|
|
q.push({tx, ty});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return d[n][m];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n >> m;
|
|
|
|
|
for (int i = 1; i <= n; i++)
|
|
|
|
|
for (int j = 1; j <= m; j++)
|
|
|
|
|
cin >> g[i][j];
|
|
|
|
|
|
|
|
|
|
cout << bfs() << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 四、加强版练习
|
|
|
|
|
|
|
|
|
|
走迷宫升级版——边的权值不同
|
|
|
|
|
|
|
|
|
|
单点时限: $2.0 sec$
|
|
|
|
|
内存限制: $256 MB$
|
|
|
|
|
|
|
|
|
|
一天,$sunny$ 不小心进入了一个迷宫,不仅很难寻找出路,而且有的地方还有怪物,但是 $sunny$ 有足够的能力杀死怪物,但是需要一定的时间,但是 $sunny$想早一点走出迷宫,所以请你帮助他计算出**最少的时间**走出迷宫,输出这个**最少时间**。
|
|
|
|
|
|
|
|
|
|
我们规定每走一格需要时间单位 $1$, 杀死怪物也需要时间 $1$, 如果不能走到出口,则输出 $impossible$. 每次走只能是上下左右 $4$ 个方向。
|
|
|
|
|
|
|
|
|
|
输入格式
|
|
|
|
|
每次首先 $2$ 个数 $n$,$m$ $(0<n,m≤200)$,代表迷宫的高和宽,然后 $n$ 行,每行 $m$ 个字符。
|
|
|
|
|
|
|
|
|
|
`S` 代码你现在所在的位置。
|
|
|
|
|
`T` 代表迷宫的出口。
|
|
|
|
|
`#` 代表墙,你是不能走的。
|
|
|
|
|
`X` 代表怪物。
|
|
|
|
|
`.` 代表路,可以走。
|
|
|
|
|
处理到文件结束。
|
|
|
|
|
|
|
|
|
|
输出格式
|
|
|
|
|
输出最少的时间走出迷宫。不能走出输出 `impossible`。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
输入样例:
|
|
|
|
|
```
|
|
|
|
|
输入样例:
|
|
|
|
|
4 4
|
|
|
|
|
S.X.
|
|
|
|
|
#..#
|
|
|
|
|
..#.
|
|
|
|
|
X..T
|
|
|
|
|
4 4
|
|
|
|
|
S.X.
|
|
|
|
|
#..#
|
|
|
|
|
..#.
|
|
|
|
|
```
|
|
|
|
|
输出样例:
|
|
|
|
|
```
|
|
|
|
|
6
|
|
|
|
|
impossible
|
|
|
|
|
```
|
|
|
|
|
($BFS$ + **优先队列**)
|
|
|
|
|
题意:**[走迷宫](https://acm.ecnu.edu.cn/problem/1224/)**,求最短路径,上下左右走一格花费`1`,走到有怪的格子花费`2`.
|
|
|
|
|
> 用户名:$littlehb$ 密码:旧密码
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 思路
|
|
|
|
|
将每一点的坐标和由起点到该点的距离存入结构体.
|
|
|
|
|
由起点开始,将该点存入优先队列,以到起点的距离`dis`为优先级,每次取出`dis`最小的,向外扩散。
|
|
|
|
|
相当于第一轮得出所有到起点距离为`1`的点,第二轮得出所有到起点距离为`2`的点。
|
|
|
|
|
注意:对普通的最短路问题,由于每个各自的花费相同,因此每次存入的点优先级都相同.
|
|
|
|
|
故不需要使用优先队列,但本题存在有无怪物的区别,每次存入的格子的优先级可能不同,故使用优先队列。
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
const int N = 210;
|
|
|
|
|
|
|
|
|
|
struct Node {
|
|
|
|
|
int x, y; // 坐标
|
|
|
|
|
int dis; // 距离
|
|
|
|
|
bool const operator<(const Node &b) const {
|
|
|
|
|
return dis > b.dis;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// 问:优先队列默认是大顶堆,如果想距离小的在前怎么办呢?
|
|
|
|
|
// 答:重载小于操作符。对比距离,谁的距离大谁就小。
|
|
|
|
|
|
|
|
|
|
char g[N][N]; // 地图
|
|
|
|
|
int sx, sy, tx, ty; // 起点坐标与终点坐标
|
|
|
|
|
|
|
|
|
|
int dx[] = {1, 0, -1, 0};
|
|
|
|
|
int dy[] = {0, 1, 0, -1};
|
|
|
|
|
int n, m; // 代表迷宫的高和宽
|
|
|
|
|
|
|
|
|
|
void bfs() {
|
|
|
|
|
priority_queue<Node> q; // 在priority_queue中,优先队列默认是大顶堆
|
|
|
|
|
Node st{sx, sy, 0};
|
|
|
|
|
g[sx][sy] = '#';
|
|
|
|
|
q.push(st);
|
|
|
|
|
|
|
|
|
|
while (q.size()) {
|
|
|
|
|
Node u = q.top();
|
|
|
|
|
q.pop();
|
|
|
|
|
// 若已找到,则退出
|
|
|
|
|
if (u.x == tx && u.y == ty) {
|
|
|
|
|
cout << u.dis << endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
int x = u.x + dx[i];
|
|
|
|
|
int y = u.y + dy[i];
|
|
|
|
|
Node t = {x, y, 0};
|
|
|
|
|
|
|
|
|
|
if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] != '#') {
|
|
|
|
|
if (g[x][y] == 'X')
|
|
|
|
|
t.dis = u.dis + 2;
|
|
|
|
|
else
|
|
|
|
|
t.dis = u.dis + 1;
|
|
|
|
|
// 走过就覆盖掉
|
|
|
|
|
g[t.x][t.y] = '#';
|
|
|
|
|
// 新结点入队列
|
|
|
|
|
q.push(t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 没有结果,输出不可能到达
|
|
|
|
|
printf("impossible\n");
|
|
|
|
|
}
|
|
|
|
|
int main() {
|
|
|
|
|
while (cin >> n >> m) {
|
|
|
|
|
for (int i = 0; i < n; i++) cin >> g[i]; // 按字符串读入
|
|
|
|
|
for (int i = 0; i < n; i++)
|
|
|
|
|
for (int j = 0; j < m; j++) {
|
|
|
|
|
if (g[i][j] == 'S') sx = i, sy = j;
|
|
|
|
|
if (g[i][j] == 'T') tx = i, ty = j;
|
|
|
|
|
}
|
|
|
|
|
bfs();
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|