LeetCode 获取所有钥匙的最短路径(位操作+广度优先搜索)

版权声明:本文为博主原创文章,博客地址:https://blog.csdn.net/qq_41855420,未经博主允许不得转载。 https://blog.csdn.net/qq_41855420/article/details/91345712

给定一个二维网格 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],每个钥匙都对应一个不同的字母,正好打开一个对应的锁。

\color{blue}思路分析: 典型的图搜索题,由于钥匙是随机放置,因此一般不存在最优路径决策问题,只能使用搜索算法(说白了就是蛮力法),在解空间中进行搜索最优解。

由于题干中说明钥匙个数不多于6个,因此我们可以使用位操作记录已经获得的的钥匙,比不keyStaus = 10 = “1010”(10的二进制串),表示钥匙’b’、‘d’已经获得了。('b' == 'a' + 1记录为1 左移1位 == 2)('d' == ‘a’ + 3记录为1左移3位 == 8)

算法描述:

第一步:扫描grid,获取起点位置,以及钥匙的个数
第二步:使用队列进行辅助广度优先搜索,将起始状态放入队列
第三步:广度优先搜索核心算法,每次将队列中的所有状态都转向下一个状态,如果这个状态没有访问过,则放入队列。
//自定义 状态 结构体<row, col, keyStaus>
struct MyNode{
    int row;//<row, col> 确定位置
    int col;
    int keyStaus;//已经得到的钥匙
    MyNode(int _row, int _col, int _keyStaus){
        row = _row;
        col = _col;
        keyStaus = _keyStaus;
    }
};
class Solution {
public:
    int shortestPathAllKeys(vector<string>& grid) {
        int keyCount = 0;//记录地图中钥匙个数
        pair<int, int> startLocaltion;//记录地图中起始点的位置
        int rowSize = grid.size(), colSize = grid[0].size();
        //第一步:扫描grid,获取起点位置,以及钥匙的个数
        for (int row = 0; row < rowSize; ++row){
            for (int col = 0; col < colSize; ++col){
                if (grid[row][col] >= 'a' && grid[row][col] <= 'z'){
                    keyCount += 1;
                }
                else if (grid[row][col] == '@'){
                    startLocaltion = {row, col};
                }
            }
        }
        queue<MyNode> myQue;//广度优先搜索辅助队列
        //allKeyStaus 表示的是使用每一个位记录一个钥匙,当所有钥匙都获取时的状态
        int allKeyStaus = (1 << keyCount) - 1, minSteps = 1;
        vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};//上、下、左、右四个方向
        myQue.push(MyNode(startLocaltion.first, startLocaltion.second, 0));//将起始点状态放入队列
        vector<vector<vector<int>>> visited(rowSize, vector<vector<int>>(colSize, vector<int>(allKeyStaus + 1, 0)));//标记数组,visited[row][col][keyStaus]表示这个状态已经访问过
        visited[startLocaltion.first][startLocaltion.second][0] = 1;//标记起始状态已经访问
        while (!myQue.empty()){
        	//将当前队列中的所有状态转移为下一个可达的装填
            for (int i = myQue.size(); i > 0; --i){
                MyNode front = myQue.front();//获取当前队头
                myQue.pop();
                //上、下、左、右四个方向进行转移
                for (const auto &direction : directions){
                    int nextRow = front.row + direction.first;
                    int nextCol = front.col + direction.second;
                    //不能出界或者走到障碍物
                    if (nextRow < 0 || nextRow >= rowSize || nextCol < 0 || nextCol >= colSize || grid[nextRow][nextCol] == '#'){
                        continue;
                    }
                    if (grid[nextRow][nextCol] >= 'A' && grid[nextRow][nextCol] <= 'Z'){
                    	//大写字母表示需要钥匙的房间
                        int needKey = 1 << (grid[nextRow][nextCol] - 'A');//需要的钥匙
                        if ((front.keyStaus & needKey) != needKey){//如果之前没有获取这个钥匙,则不能到达这个状态
                            continue;
                        }
                    }
                    MyNode nextNode(nextRow, nextCol, front.keyStaus);
                    if (grid[nextRow][nextCol] >= 'a' && grid[nextRow][nextCol] <= 'z'){
                    	//小写字母表示钥匙
                        nextNode.keyStaus |= (1 << (grid[nextRow][nextCol] - 'a'));//更新获得的钥匙
                        if (nextNode.keyStaus == allKeyStaus){//判断是否已经获取的了所有的钥匙
                            return minSteps;
                        }
                    }
                    if (visited[nextNode.row][nextNode.col][nextNode.keyStaus] == 0){
                    //如果这个状态没有访问过,放入队列
                        myQue.push(nextNode);
                        visited[nextNode.row][nextNode.col][nextNode.keyStaus] = 1;
                    }
                }
            }
            ++minSteps;//步骤数自增
        }
        return -1;
    }
};

在这里插入图片描述

扫描二维码关注公众号,回复: 6393721 查看本文章

猜你喜欢

转载自blog.csdn.net/qq_41855420/article/details/91345712
今日推荐