【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;
}