leetcode.864 获取所有钥匙的最短路径 - bfs+状态压缩+最短路+位运算

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

困难 看着三叶姐的题解理解的 理解上没什么大问题 主要卡在位运算上

其他思路是传统的bfs做法

第一步:解决钥匙问题

因为要求捡完所有钥匙的最短路径,锁可以不开完,但钥匙要捡完

  • 题目规定【钥匙数量不超过6,按字母顺序排列】,我们可以用二进制数state来表示当前收集到钥匙的情况
  • 如果state的二进制中第k位为1,代表编号k的钥匙已被收集
  • 如果state的二进制中第k为为0,代表编号k的钥匙未被收集

通过位运算可以进行钥匙检测更新钥匙收集状态

  • 钥匙检测:(state>>k)&1  也就是取出第k位,判断是否为1 ,是即为拥有编号k的钥匙
  • 更新钥匙收集状态:state|=1<<k 将state的第k位设置为1,代表收集到编号为k的钥匙

第二步:准备工作

  • pos[x][y][state]   记录的是当前步的最短路 一开始要都初始化所有的点的pos为无穷大
  • dirs[][]   方向数组,控制上下左右移动

第三步:BFS 

  • 首先将起点@入队  并遍历全图  记录钥匙总数key
  • 由起点开始上下左右扩展 如果越界、遇到锁没钥匙、走的步数大于之前的最短路、撞墙四种情况 continue 即这条路走不通 放弃
  • 遇到钥匙时,需要更新钥匙状态state
  • 如果钥匙状态state每一位全为1 说明钥匙收集完毕 只要再走一步(step+1)就完成任务
  • 将当前点入队 继续下一轮扩展
class Solution {

    static int N=30,K=6,INF=0x3f3f3f3f;
    static int[][] dirs=new int[][]{{1,0},{-1,0},{0,-1},{0,1}};
    //x y代表坐标,s代表当前钥匙状态 pos表示当前步的最短路
    static int[][][] pos=new int[N][N][1<<K];

    public int shortestPathAllKeys(String[] g) 
    {   
        Deque<int[]> q=new ArrayDeque<>();
        int m=g.length,n=g[0].length();
        int key=0;
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
            {
                Arrays.fill(pos[i][j],INF); //初始化每个格最短距离为无穷大
                char ch=g[i].charAt(j);
                if(ch=='@')
                {
                    q.offerLast(new int[]{i,j,0});
                    pos[i][j][0]=0;
                }else if(ch>='a'&&ch<='z') key++;
            }

        while(!q.isEmpty())
        {
            int[] t=q.pollFirst();
            int x=t[0],y=t[1],state=t[2];
            int step=pos[x][y][state];
            for(int[] dir:dirs)
            {
                int nx=x+dir[0],ny=y+dir[1];
                if(nx<0||nx>=m||ny<0||ny>=n) continue;
                char c=g[nx].charAt(ny);
                if(c=='#') continue;
                if((c>='A'&&c<='Z')&&(state>>(c-'A')&1)==0) continue; //如果遇到锁但是没有钥匙 跳过

                int next=state; //因为state在for循环外定义的,所以不能直接更改,需要先定义变量接收
                if(c>='a'&&c<='z') 
                    next|=1<<(c-'a'); //如果是钥匙 则更新状态

                if(next==(1<<key)-1) return step+1; //如果钥匙状态全为1 说明再走一步钥匙搜集完毕
                if(step+1>=pos[nx][ny][next]) continue; //如果之前有更短的走法则跳过不走

                pos[nx][ny][next]=step+1;
                q.offerLast(new int[]{nx,ny,next});
            }
        }
        return -1;

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值