困难 看着三叶姐的题解理解的 理解上没什么大问题 主要卡在位运算上
其他思路是传统的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;
}
}