LeetCode 864. 获取所有钥匙的最短路径

94 篇文章 0 订阅

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. 1 <= grid.length <= 30
  2. 1 <= grid[0].length <= 30
  3. grid[i][j] 只含有 '.', '#', '@', 'a'-``'f``' 以及 'A'-'F'
  4. 钥匙的数目范围是 [1, 6],每个钥匙都对应一个不同的字母,正好打开一个对应的锁。

最短路径-BFS,状态压缩

在普通的迷宫上添加了钥匙和门的选项,用一个数来保存拥有的钥匙

v i s vis vis 数组中要新增一维来保存钥匙的状态,因为拿完钥匙是有可能要回退的,范围设为 [ 0 , 1 &lt; &lt; 7 ] [0,1&lt;&lt;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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值