【PDD4 迷宫寻路】

PDD4 迷宫寻路

来着牛客网的一道拼多多笔试题,有门有锁的一个迷宫问题。

题目描述

假设一个探险家被困在了地底的迷宫之中,要从当前位置开始找到一条通往迷宫出口的路径。迷宫可以用一个二维矩阵组成,有的部分是墙,有的部分是路。迷宫之中有的路上还有门,每扇门都在迷宫的某个地方有与之匹配的钥匙,只有先拿到钥匙才能打开门。请设计一个算法,帮助探险家找到脱困的最短路径。如前所述,迷宫是通过一个二维矩阵表示的,每个元素的值的含义如下 0-墙,1-路,2-探险家的起始位置,3-迷宫的出口,大写字母-门,小写字母-对应大写字母所代表的门的钥匙。

输入描述:
迷宫的地图,用二维矩阵表示。第一行是表示矩阵的行数和列数M和N 后面的M行是矩阵的数据,每一行对应与矩阵的一行(中间没有空格)。M和N都不超过100, 门不超过10扇。

输出描述:
路径的长度,是一个整数

示例1
输入:

5 5
02111
01a0A
01003
01001
01111

输出:

7

解题思路

普通的迷宫问题比较简单,利用 bfs 只要到达就是最少步数的特点,直接就能解决。这道题的难点在于门的出现,要求必须有钥匙才能通过。而如果要拿到钥匙的话,就不能保证每个点访问一次,也就没法保证走到的所有点都是最短距离……

这里的话我们用一种形象的描述来解决这个问题:现在要从一楼出发去某个房间,路上有一些门没有钥匙。但是中间有一些楼梯可以让我们上楼,上去之后可以在上面的楼层绕道门后再下来……

根据这种描述,我们可以得到灵感:原本的迷宫问题是在每个点维护最少步数这一个状态,现在我们在每个点维护多重状态,也就是持有k把钥匙时的最少步数。这样每次经过钥匙更新一下状态,每次到门都去检查是否有钥匙,来确定此时能不能通过。当然,这里用于过滤重复访问的 vis 数组也要增加一个维度。

相应的,其他类似改进型,也都是通过状态的增加来解决的。

参考代码

#include <assert.h>
#include <string.h>
#include <iostream>
#include <queue>
using namespace std;

const int dir[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };

int m, n;
char ndarr[103][103];
bool vis[101][101][1<<10];
int startX, startY;

struct State {
    int x, y;
    int step;
    int keys; // 使用二进制表示持有的钥匙列表
};

int bfs() {
    assert(startX >= 0 && startY >= 0);
    queue<State> q;
    q.push({startX, startY, 0, 0});
    memset(vis, false, sizeof(vis));
    while (!q.empty()) {
        auto [x, y, step, key] = q.front();
        q.pop();
        if (ndarr[x][y] == '3') {
            return step;
        }
        for (int k=0; k<4; k++) {
            int i = x + dir[k][0];
            int j = y + dir[k][1];
            if (i<0 || i>=m || j<0 || j>=n) continue;
            if (ndarr[i][j] == '3') {
                return step+1;
            }
            if (ndarr[i][j] == '0') continue;
            if (isupper(ndarr[i][j])
                && (0 == (key & (1 << (tolower(ndarr[i][j]) - 'a'))))) continue;
            int newkey = key;
            if (islower(ndarr[i][j])) {
                newkey |= (1 << (ndarr[i][j] - 'a'));
            }
            if (!vis[i][j][newkey]) {
                vis[i][j][newkey] = true;
                q.push({i, j, step+1, newkey});
            }
        }
    }
    return -1;
}

int main() {
    cin >> m >> n;
    startX = -1;
    startY = -1;
    for (int i=0; i<m; i++) {
        cin >> ndarr[i];
        if (startX < 0) {
            for (int j=0; j<n; j++) {
                if (ndarr[i][j] == '2') {
                    startX = i;
                    startY = j;
                    break;
                }
            }
        }
    }
    printf("%d\n", bfs());
    return 0;
}
posted @ 2021-09-01 00:02  与MPI做斗争  阅读(118)  评论(0编辑  收藏  举报