题目描述:
蒜头君要回家,但是他家的钥匙在他的朋友花椰妹手里,他要先从花椰妹手里取得钥匙才能回到家。花椰妹告诉他:“你家的钥匙被我复制了很多个,分别放在不同的地方。”
蒜头君希望能尽快回到家中,他首先需要取得任意一把钥匙,请你帮他计算出回家所需要的最短路程。
蒜头君生活的城市可以看做是一个 n×m 的网格,其中有道路有障碍,钥匙和家所在的地方可以看做是道路,可以通过。蒜头君可以在城市中沿着上下左右 4 个方向移动,移动一个格子算做走一步。
输入输出要求:
输入格式
第一行有两个整数 n,m。城市的地图是 n 行 m 列。(1≤n,m≤2000)
接下来的 n 行,每行 m 个字符,代表城市的地图。'.'
代表道路,'#'
代表障碍物,'S'
代表蒜头君所在的位置,'T'
代表蒜头家的位置,'P'
代表钥匙的位置。除了障碍物以外,别的地方都可以通过。题目保证蒜头君至少有一条路径可以顺利拿到钥匙并且回家。
输出格式
输出蒜头回家要走的最少步数,占一行。
本质上还是BFS
自己的原始代码如下,想法是利用两次BFS,先找到所有钥匙的最短路径,再依此为基础再回家,但是当数据过大会有溢出,因此平台提交也是7过4
代码如下:
#include<stdio.h>
int n, m;
char a[2010][2010];
int vis[2010][2010];
int step[4][2] = { {1,0},{0,-1},{-1,0},{0,1} };
typedef struct qu {
int x;
int y;
int d;
}Qu;
int in(int i, int j)
{
return i >= 0 && i < n&& j >= 0 && j < m;
}
Qu q[4000000];
int front, rear;
//bfs1找所有钥匙,找到钥匙入队
void bfs1(int sx, int sy)
{
int front1, rear1;
front1 = rear1 = -1;
rear1++;
Qu q1[4000000];
q1[rear1].x = sx;
q1[rear1].y = sy;
q1[rear1].d = 0;
vis[sx][sy] = 1;
while (front1 < rear1) {
front1++;
Qu now = q1[front1];
for (int i = 0; i < 4; i++) {
Qu next;
next.x = now.x + step[i][0];
next.y = now.y + step[i][1];
next.d = now.d + 1;
if (in(next.x, next.y) && !vis[next.x][next.y] && a[next.x][next.y] != '#') {
rear1++;
q1[rear1] = next;
vis[next.x][next.y] = 1;
//找到钥匙就入队
if (a[next.x][next.y] == 'P') {
rear++;
q[rear] = next;
}
}
}
}
}
void bfs2()
{
int dist = 40000010;
for (int i = 0; i <= rear; i++) {
int vis1[2010][2010];
for (int k = 0; k < n; k++) {
for (int j = 0; j < m; j++) {
vis1[k][j] = 0;
}
}
Qu q2[4000000];
int front2, rear2;
front2 = rear2 = -1;
rear2++;
q2[rear2] = q[i];
vis1[q[i].x][q[i].y] = 1;
while (front2 < rear2) {
front2++;
Qu now = q2[front2];
if (a[now.x][now.y] == 'T') {
if (now.d < dist) dist = now.d;
break;
}
else {
for (int j = 0; j < 4; j++) {
Qu next;
next.x = now.x + step[j][0];
next.y = now.y + step[j][1];
next.d = now.d + 1;
if (in(next.x, next.y) && !vis1[next.x][next.y] && a[next.x][next.y] != '#') {
rear2++;
q2[rear2] = next;
vis1[next.x][next.y] = 1;
}
}
}
}
}
printf("%d", dist);
}
int main()
{
int sx, sy;
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) scanf("%s", a[i]);
for (int i = 0; i < n; i++) {
int flag = 0;
for (int j = 0; j < m; j++) {
if (a[i][j] == 'S') {
sx = i;
sy = j;
flag = 1;
break;
}
}
if (flag) break;
}
front = rear = -1;
bfs1(sx, sy);
bfs2();
return 0;
}
参考了别的大佬的代码后发现可以用vis三维数组,只进行一次BFS进行求解问题
代码如下:
#include<stdio.h>
#include<stdlib.h>
#define MAX 2010
int n, m, vis[MAX][MAX][2]; //第三个维度标志在这个位置是否有钥匙
char a[MAX][MAX];
int step[4][2] = { {-1, 0}, {0, -1}, {1, 0}, {0, 1} };
int in(int x, int y) {
return 0 <= x && x < n && 0 <= y && y < m;
}
typedef struct qu {
int x, y, d, flag; //flag用来标记是否有钥匙
}Qu;
Qu q[4000010];
int bfs(int sx, int sy) {
int front = -1, rear = -1;
rear++;
Qu t = {sx, sy, 0,0 };
q[rear] = t;
vis[sx][sy][0] = 1;
while (front < rear) {
front++;
Qu now = q[front];
if (a[now.x][now.y] == 'T' && now.flag == 1) {
return now.d;
}
for (int i = 0; i < 4; i++) {
Qu next;
next.flag = now.flag;
next.x = now.x + step[i][0];
next.y = now.y + step[i][1];
next.d = now.d + 1;
if (in(next.x, next.y) && !vis[next.x][next.y][now.flag] && a[next.x][next.y] != '#') {
if (a[next.x][next.y] == 'P') {
next.flag = 1;
rear++;
q[rear] = next;
}
else {
rear++;
q[rear] = next;
}
vis[next.x][next.y][next.flag] = 1;
}
}
}
return -1;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%s", a[i]);
}
int sx, sy;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] == 'S') {
sx = i;
sy = j;
}
}
}
printf("%d\n", bfs(sx, sy));
return 0;
}
总结:做了很多有附加条件的BFS,其实很多都可以利用多维数组解题,多一个维度也就是多了一种状态,这样便于理解