[leetcode]864. 获取所有钥匙的最短路径(状态压缩bitmask+bfs)

题目链接

题意

给定一个 n × m n\times m n×m的地图
‘.’ 代表一个空房间
‘#’ 代表一堵墙
‘@’ 是起点
小写字母代表钥匙
大写字母代表锁
钥匙和门互为大小写

目的是收集所有钥匙 不一定要把门都打开
求最少需要几步
数据范围: n , m ≤ 30 , k 对钥匙和门 , k ≤ 6 n,m\leq 30,k对钥匙和门,k\leq 6 n,m30,k对钥匙和门,k6

思路

  • 数据很小 显然搜索 但是我一开始写的dfs错了 求最少步数还是bfs更合适

  • 本题的重点是vis有第三个状态 就是当前手中的钥匙情况
    而不是简单的标记走过的点不能再走 因为可能需要走到一个角落拿钥匙 还需要原路返回 所以点可以重复走
    但是 如果拥有钥匙情况不变 就不能重复走某个点 因为这样是无意义的 浪费步数

  • 一定要想清楚这一维再开始写代码

Code

#define pii pair<int,int>
#define ar2 array<int,2>
#define ar3 array<int,3>
#define ar4 array<int,4>
#define endl '\n'
void cmax(int &a,int b){a=max(a,b);};
void cmin(int &a,int b){a=min(a,b);};
const int N=40,MOD=1e9+7,INF=0x3f3f3f3f;const long long LINF=LLONG_MAX;const double eps=1e-6;
char g[N][N];
int n,m;
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
bool vis[N][N][1<<7];

class Solution {
public:
    int k=0;
    int sx,sy;
    int shortestPathAllKeys(vector<string>& grid) {
        n=grid.size(),m=grid[0].size();
        memset(vis,0,sizeof vis);

        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                g[i+1][j+1]=grid[i][j];
            }
        }

        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(g[i][j]=='@') sx=i,sy=j;
                if(isupper(g[i][j])) k++;
            }
        }

        int ans=bfs();
        return ans;
    }

    int bfs(){
        queue<ar4>q;
        q.push({sx,sy,0,0});//steps keys
        vis[sx][sy][0]=1;

        int finalst=(1<<k)-1;
        

        while(q.size()){
            auto [x,y,steps,key]=q.front();q.pop();
            if(key==finalst){
                return steps;
            }

            for(int i=0;i<4;i++){
                int tx=x+dx[i],ty=y+dy[i];
                if(tx<1||tx>n||ty<1||ty>m||g[tx][ty]=='#') continue;
                if(vis[tx][ty][key]) continue;
                
                char c=g[tx][ty];
                int newkey=key;
                
                if(islower(c)){
                    newkey|=(1<<(c-'a'));
                }

                if(isupper(c)){
                    if((key&(1<<(c-'A')))==0) continue;
                }

                vis[tx][ty][newkey]=1;
                q.push({tx,ty,steps+1,newkey});
            }
        }
        return -1;
    }
};

实现细节

如何使用bitmask状态压缩 来表示当前钥匙状态?

在二进制意义上看 最多6个钥匙 也就是最多 ( 1 < < 6 ) 10 = 2 6 = 64 (1<<6)_{10}=2^6=64 (1<<6)10=26=64
v i s 中 30 ∗ 30 ∗ 64 空间完全开得下 vis中30*30*64空间完全开得下 vis303064空间完全开得下

说回bitmask 二进制上每一位代表这一位所对应的钥匙是否获得 通过c-'a’进行映射

队列存储四维内容

保存这个状态的坐标 走的步数 当前钥匙状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值