图论17(Leetcode864.获取所有钥匙的最短路径)
用二进制表示获得的钥匙,假设n=钥匙个数
000000000代表没有钥匙,0000000001代表有idx为1的钥匙,0000000011代表有idx=1,2的钥匙
(这方法巧妙又复杂..
代码:
class Solution {
static int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
public int shortestPathAllKeys(String[] grid) {
int m = grid.length, n = grid[0].length();
int startx = 0,starty = 0;
Map<Character, Integer> keyToIndex = new HashMap<>();
//存钥匙的字母和对应的idx序号
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[i].length();j++){
if(grid[i].charAt(j)=='@'){
startx = i;
starty = j;
}else if(Character.isLowerCase(grid[i].charAt(j))){
if(!keyToIndex.containsKey(grid[i].charAt(j))){
int idx = keyToIndex.size();
keyToIndex.put(grid[i].charAt(j), idx);
}
}
}
}
Queue<int[]> queue = new ArrayDeque<int[]>();
int[][][] dist = new int[m][n][1<<keyToIndex.size()];
//第三维是2的size次方 列举钥匙的所有可能
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
Arrays.fill(dist[i][j],-1);
}
}
queue.offer(new int[]{startx,starty,0});
dist[startx][starty][0] = 0;
while(!queue.isEmpty()){
int[] arr = queue.poll();
int x = arr[0],y = arr[1],mask = arr[2];//mask是钥匙的排列
for(int i=0;i<4;i++){
int nx = x + dirs[i][0];
int ny = y + dirs[i][1];
if(nx>=0 && nx<m && ny>=0 && ny<n &&grid[nx].charAt(ny)!='#'){
if(grid[nx].charAt(ny)=='.'||grid[nx].charAt(ny)=='@'){
if(dist[nx][ny][mask] == -1){
dist[nx][ny][mask] = dist[x][y][mask]+1;
queue.offer(new int[]{nx,ny,mask});
}
}else if(Character.isLowerCase(grid[nx].charAt(ny))){
int idx = keyToIndex.get(grid[nx].charAt(ny));
if(dist[nx][ny][mask|(1<<idx)] == -1){
dist[nx][ny][mask|(1<<idx)] = dist[x][y][mask]+1;
if((mask|(1<<idx))==(1<<keyToIndex.size())-1){
return dist[nx][ny][mask|(1<<idx)];
}
queue.offer(new int[]{nx,ny,mask|(1<<idx)});
}
}else{
int idx = keyToIndex.get(Character.toLowerCase(grid[nx].charAt(ny)));
if((mask&(1<<idx))!=0&&dist[nx][ny][mask]==-1){
dist[nx][ny][mask] = dist[x][y][mask]+1;
queue.offer(new int[]{nx,ny,mask});
}
}
}
}
}
return -1;
}
}