LeetCode 864. 获取所有钥匙的最短路径
给定一个二维网格 grid
。 "."
代表一个空房间, "#"
代表一堵墙, "@"
是起点,("a"
, "b"
, …)代表钥匙,("A"
, "B"
, …)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6
,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1
。
示例 1:
输入:["@.a.#","###.#","b.A.B"]
输出:8
示例 2:
输入:["@..aA","..B#.","....b"]
输出:6
提示:
1 <= grid.length <= 30
1 <= grid[0].length <= 30
grid[i][j]
只含有'.'
,'#'
,'@'
,'a'-``'f``'
以及'A'-'F'
- 钥匙的数目范围是
[1, 6]
,每个钥匙都对应一个不同的字母,正好打开一个对应的锁。
最短路径-BFS,状态压缩
在普通的迷宫上添加了钥匙和门的选项,用一个数来保存拥有的钥匙
v i s vis vis 数组中要新增一维来保存钥匙的状态,因为拿完钥匙是有可能要回退的,范围设为 [ 0 , 1 < < 7 ] [0,1<<7] [0,1<<7] 因为最多有6把钥匙嘛
碰到门时判断以下拥有的钥匙能否打开门,不能就 continue
做的时候碰到的问题(就是瞎敲,没有对状态理解深入到肌肉记忆(滑稽)):
每次从fa
结点发展四个方向son
时,son
的钥匙状态一定要是fa
的,对于所有状态来说都是必要的,要对所有变量一视同仁,错误的定义位置见代码
BFS错误好查,把能加入队列的dr,dc
点输出,然后模拟一下就知道哪错了
0 1 2 3 4
0 @ . . . a
1 . # # # A
2 b . B C c
在[1,0]点先向下走,到[2,0]拿到了b钥匙那么之后再往上走[0,0]时状态就不同了,所以会有错误
#include <queue>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
static const auto io_sync_off = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return nullptr;
}();
const int maxn = 32;
const int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
bool vis[maxn][maxn][1 << 7]; // 行,列,钥匙状态
int keyNum = 0; // 总钥匙数
struct point
{
int r, c; // 行,列
int step, key; // 步数,拥有钥匙的状态
point(int r = 0, int c = 0, int step = 0, int key = 0) : r(r), c(c), step(step), key(key) {}
};
point start;
int bfs(int n, int m, vector<string> &grid)
{
queue<point> q;
q.push(start);
vis[start.r][start.c][0] = true;
while (!q.empty())
{
point cur = q.front();
q.pop();
if (cur.key == keyNum)
return cur.step;
/*int curKey = cur.key;*/ //错误的定义位置
for (int i = 0; i < 4; ++i)
{
int dr = cur.r + dir[i][0];
int dc = cur.c + dir[i][1];
int curKey = cur.key; // !!!注意每次的钥匙状态要继承目前点!!!
if (dr < 0 || dc < 0 || dr >= n || dc >= m || grid[dr][dc] == '#' || vis[dr][dc][curKey])
continue;
char ch = grid[dr][dc];
if (ch >= 'a' && ch <= 'f')
curKey |= 1 << (ch - 'a'); // 拿到钥匙
if (ch >= 'A' && ch <= 'F')
{
int doorNum = ch - 'A';
if (!(curKey >> doorNum & 1)) // 拥有的钥匙打不开门
continue;
}
vis[dr][dc][curKey] = true;
q.push(point{dr, dc, cur.step + 1, curKey});
}
}
return -1;
}
int main()
{
int n, m;
cin >> n >> m;
vector<string> grid(n);
for (int i = 0; i < n; ++i)
cin >> grid[i];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
char ch = grid[i][j];
if (ch == '@')
start = point{i, j, 0, 0}; //初始点
if (ch >= 'a' && ch <= 'f')
keyNum |= 1 << (ch - 'a'); //获取所有钥匙状态
}
cout << bfs(n, m, grid) << endl;
return 0;
}