跳刀抓人(分层图BFS)
题意
给定一个\(n \times m\)的网格图,其中一些格子是平地,可以走;一些格子是障碍物,不能走。给定起点\(S\)和终点\(T\),现在要从\(S\)走到\(T\)。
现在有三种行动方式,但是无论是哪种行动方式,都不能超出地图的范围:
- 移动到上下左右某一个相邻的平地格子,花费时间\(1s\)。
- 使用道具,可以移动到距离当前点切比雪夫距离不超过\(3\)的平地格子(切比雪夫距离:\(max(|x1 - x2|, |y1 - y2|)\)),花费时间\(1s\)。但是这个道具有冷却时间\(7s\)。
- 原地不动
道具第一次使用可以不用冷却。问从\(S\)到\(T\)的最小花费时间。
思路
\(2 \leq n, m \leq 100\)
思路
分层图最短路板子题。将原图复制\(7\)份,这\(8\)个图从\(0\)到\(7\)标号,表示当前层道具的冷却时间。
在BFS的过程中,将位置和冷却时间传进队列。同时距离数组也是两维的\(\{p, t\}\),表示到当前位置\(p\),道具冷却时间为\(t\)需要的最短时间。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> pii;
typedef pair<pii, int> ppi;
const int N = 110, M = 10;
int n, m;
int sx, sy, ex, ey;
char s[N][N];
int d[N][N][M];
int dx[5] = {-1, 1, 0, 0, 0};
int dy[5] = {0, 0, -1, 1, 0};
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%s", s[i] + 1);
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= m; j ++) {
if(s[i][j] == 'A') {
sx = i;
sy = j;
}
if(s[i][j] == 'B') {
ex = i;
ey = j;
}
}
}
memset(d, 0x3f, sizeof d);
d[sx][sy][0] = 0;
queue<ppi> que;
que.push({{sx, sy}, 0});
while(que.size()) {
auto t = que.front();
que.pop();
int tx = t.first.first, ty = t.first.second;
int time = t.second;
for(int i = 0; i < 5; i ++) {
int x = tx + dx[i], y = ty + dy[i];
if(x < 1 || x > n || y < 1 || y > m) continue;
if(s[x][y] == '#') continue;
if(d[x][y][max(0, time - 1)] > d[tx][ty][time] + 1) {
d[x][y][max(0, time - 1)] = d[tx][ty][time] + 1;
que.push({{x, y}, max(0, time - 1)});
}
}
if(!time) {
for(int i = tx - 3; i <= tx + 3; i ++) {
for(int j = ty - 3; j <= ty + 3; j ++) {
if(i < 1 || i > n || j < 1 || j > m) continue;
if(s[i][j] == '#') continue;
if(max(abs(i - tx), abs(j - ty)) > 3) continue;
if(d[i][j][7] > d[tx][ty][time] + 1) {
d[i][j][7] = d[tx][ty][time] + 1;
que.push({{i, j}, 7});
}
}
}
}
}
int ans = 0x3f3f3f3f;
for(int i = 0; i <= 7; i ++) ans = min(ans, d[ex][ey][i]);
if(ans == 0x3f3f3f3f) ans = -1;
printf("%d\n", ans);
return 0;
}