找钥匙回家问题

题目描述:

蒜头君要回家,但是他家的钥匙在他的朋友花椰妹手里,他要先从花椰妹手里取得钥匙才能回到家。花椰妹告诉他:“你家的钥匙被我复制了很多个,分别放在不同的地方。” 

蒜头君希望能尽快回到家中,他首先需要取得任意一把钥匙,请你帮他计算出回家所需要的最短路程。

蒜头君生活的城市可以看做是一个 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,其实很多都可以利用多维数组解题,多一个维度也就是多了一种状态,这样便于理解

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值