Description
给定一个张
n
×
m
n \times m
n×m 的网格图,有一束激光从左上角向右射出,每次遇到 #
。你可以选择使用一次魔法,让光线从 #
的四个方向射出,也不用魔法可以直接穿过 #
。求最少用多少次魔法才能让激光从第
n
n
n 行向右边射出。
Solution
好久前写的题了,码风都不一样。
先考虑转换,一行经过一个 #
可以以一的代价到一列,反之同理。所以可以建一个二分图,一个点集代表行一个点集代表列。如果一个格子上有 #
,那么将对应的行列相连。再用 bfs 跑一次从第一行到第
n
n
n 行的最短路即可。
还有 01 BFS 的做法,这就直接了当了,对于一个 #
,从四个方向射出的花费为一,否则没有花费,直接上 01 BFS。
Code
01 BFS
#include <bits/stdc++.h>
using namespace std;
const int N = 1000 + 5;
#define INF (1 << 29)
int n, m;
char grid[N][N];
int dist[N][N][4];
int vis[N][N][4];
int fx[] = {1, -1, 0, 0};
int fy[] = {0, 0, 1, -1};
deque <int> q;
void add_front(int x, int y, int dir, int d) {
if (d < dist[x][y][dir]) {
dist[x][y][dir] = d;
q.push_front(dir);
q.push_front(y);
q.push_front(x);
}
}
void add_back(int x, int y, int dir, int d) {
if (d < dist[x][y][dir]) {
dist[x][y][dir] = d;
q.push_back(x);
q.push_back(y);
q.push_back(dir);
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> grid[i];
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
for (int k = 0; k < 4; k++) dist[i][j][k] = INF;
add_front(n - 1, m - 1, 3, 0);
while (!q.empty()) {
int x = q[0], y = q[1], dir = q[2];
q.pop_front(); q.pop_front(); q.pop_front();
if (vis[x][y][dir]) continue;
vis[x][y][dir] = true;
int d = dist[x][y][dir];
int nx = x + fx[dir], ny = y + fy[dir];
if (nx >= 0 && nx < n && ny >= 0 && ny < m) add_front(nx, ny, dir, d);
if (grid[x][y] == '#')
for (int i = 0; i < 4; i++)
if (i != dir) add_back(x, y, i, d + 1);
}
if (dist[0][0][3] == INF) cout << -1 << endl;
else cout << dist[0][0][3] << endl;
return 0;
}