获取所有钥匙的最短路径

链接
给定一个二维网格 grid ,其中:

  • ‘.’ 代表一个空房间
  • ‘#’ 代表一堵墙
  • ‘@’ 是起点
  • 小写字母代表钥匙
  • 大写字母代表锁

我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。

假设 k 为 钥匙/锁 的个数,且满足 1 <= k <= 6,字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。

返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。

分层图BFS,地图多了一维钥匙状态。在新的地图上跑BFS。
状态压缩,钥匙的状态转化为一个二进制数,1表示有,0表示没有,再转化为10进制数。
若有第n把钥匙,第三维空间开2的n次。

struct node {
    int x, y, k, step;
};
int dir[4][2] = {
    {1,0},{0,1},{0,-1},{-1,0}
};
class Solution {
public:
    int shortestPathAllKeys(vector<string>& grid) {
        int n = grid.size(), m = grid[0].size();
        int key = 0;
        node t;
        t.k = 0, t.step = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == '@') {
                    t.x = i, t.y = j;
                } else if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {
                    key |= 1 << (grid[i][j] - 'a');
                }
            }
        }
        bool vis[n][m][2 << 6];
        memset(vis, 0, sizeof(vis));
        queue<node> q;
        q.push(t);
        vis[t.x][t.y][0] = true;
        while (q.size()) {
            t = q.front();
            q.pop();
            for (int i = 0; i < 4; i++) {
                int xx = t.x + dir[i][0], yy = t.y + dir[i][1], kk = t.k;
                if (xx < 0 || xx >= n || yy < 0 || yy >= m || vis[xx][yy][kk] || grid[xx][yy] == '#') continue;
                if (grid[xx][yy] >= 'A' && grid[xx][yy] <= 'F' && !((1 << (grid[xx][yy] - 'A')) & kk)) continue;
                if (grid[xx][yy] >= 'a' && grid[xx][yy] <= 'f') {
                    kk |= 1 << (grid[xx][yy] - 'a');
                    if (kk == key) {
                        return t.step + 1;
                    }
                } 
                q.push({ xx,yy,kk,t.step + 1 });
                vis[xx][yy][kk] = true;
            }
        }
        return -1;
    }
};
github