版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Viscu/article/details/82505604
给定一个二维网格 grid。 “.” 代表一个空房间, “#” 代表一堵墙, “@” 是起点,(”a”, “b”, …)代表钥匙,(”A”, “B”, …)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
思路:看代码+解释 运用位运算来简化代码 简单题 多了一维存储钥匙的状态而已。
class Solution {
int[] dx={0,0,1,-1}; //上下左右移动
int[] dy={1,-1,0,0};
boolean[][][] vis; //前两位表示左边,后一位表示当前拥有钥匙的状态
static class Idx{
int x;
int y;
int cnt; //表示当前的步数
int keyState; //表示当前有用钥匙的状态
//怎么表示,因为有6把钥匙 所以我们用6位bit表示当前拥有钥匙的状态 eg: 000101表示当前拥有a,c钥匙
public Idx(int x, int y, int cnt, int keyState){
this.x = x;
this.y = y;
this.cnt = cnt;
this.keyState = keyState;
}
}
public int shortestPathAllKeys(String[] grid) {
int n=grid.length;
int m=grid[0].length();
Idx st=null;
int sx=0;
int sy=0;
int keySum=0;//表示状态
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
char ch=grid[i].charAt(j);
if(ch=='@'){ //寻找起点
sx=i;
sy=j;
st=new Idx(i,j,0,0);
}
if(ch>='a'&&ch<='f'){
keySum|=1<<ch-'a'; //保存钥匙的状态 样例1为000011.
}
}
}
vis=new boolean[n][m][keySum+1];
vis[sx][sy][0]=true; //初始位置标志为已经走过
Queue<Idx> queue=new LinkedList<Idx>();
queue.add(st);
while (!queue.isEmpty()){
Idx cur=queue.poll();
if(cur.keyState==keySum){ //若当前钥匙状态与之比较相同 说明已经拥有全部的钥匙
return cur.cnt; //直接返回步数
}
for(int i=0;i<4;++i){ //上下左右遍历
int xi=cur.x+dx[i];
int yi=cur.y+dy[i];
if(xi<0||yi<0||xi>=n||yi>=m||vis[xi][yi][cur.keyState]
||grid[xi].charAt(yi)=='#'){ //越界或者墙不能走
continue;
}
int keyState=cur.keyState;
char ch=grid[xi].charAt(yi);
if(ch>='a'&&ch<='f'){ //如果是钥匙 保存钥匙
keyState|=1<<(ch-'a'); // 遇到a keyState为 000001
}
if(ch>='A'&&ch<='F'){ //遇到门
int doorState=ch-'A'; // 该位运算的意义就是第几把钥匙是否存在
if((keyState>>doorState&1)!=1){ //即第ch-'A'位置上是否为1 为1说明有钥匙 左移处理
continue;
}
}
vis[xi][yi][keyState]=true; //设置当前为走过 多个状态 后面表示的是所拥有的钥匙的状态
queue.add(new Idx(xi,yi,cur.cnt+1,keyState));
}
}
return -1;
}
}