You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.0 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2022_10_2_week;
import java.util.PriorityQueue;
// 来自Airbnb、Uber
// 给定一个二维网格 grid 其中
// '.' 代表一个空房间
// '#' 代表一堵
// '@' 是起点
// 小写字母代表钥匙
// 大写字母代表锁
// 我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间
// 我们不能在网格外面行走,也无法穿过一堵墙
// 如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
// 假设 k 为 钥匙/锁 的个数且满足 1 <= k <= 6
// 字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母
// 换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁
// 另外,代表钥匙和锁的字母互为大小写并按字母顺序排列
// 返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 
// 测试链接https://leetcode.cn/problems/shortest-path-to-get-all-keys
public class Code04_ShortestPathToGetAllKeys {
// "@....#"
// "..b..B"
//
// @ . . . . #
// . . B . . B
public int shortestPathAllKeys(String[] grid) {
int n = grid.length;
char[][] map = new char[n][];
for (int i = 0; i < grid.length; i++) {
map[i] = grid[i].toCharArray();
}
int m = map[0].length;
return dijkstra(map, n, m);
}
public static int dijkstra(char[][] map, int n, int m) {
int startX = 0;
int startY = 0;
int keys = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (map[i][j] == '@') {
startX = i;
startY = j;
}
if (map[i][j] >= 'a' && map[i][j] <= 'z') {
keys++;
}
}
}
// 如果有4把钥匙
// limit = 0000..00001111
// 如果有5把钥匙
// limit = 0000..00011111
// 也就是说所有钥匙都凑齐的状态就是limit
int limit = (1 << keys) - 1;
// 用堆来维持走过的点(dijkstra标准操作)
// 维持的信息是一个个小的4维数组arr
// arr[0] : 当前来到的x坐标
// arr[1] : 当前来到的y坐标
// arr[2] : 当前收集到的钥匙状态
// arr[3] : 从出发点到当前的距离
// 堆根据距离的从小到大组织,距离小根堆
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[3] - b[3]);
boolean[][][] visited = new boolean[n][m][1 << keys];
// startX, startY, 000000
heap.add(new int[] { startX, startY, 0, 0 });
while (!heap.isEmpty()) {
int[] cur = heap.poll();
int x = cur[0];
int y = cur[1];
int s = cur[2];
int w = cur[3];
if (s == limit) {
return w;
}
if (visited[x][y][s]) {
continue;
}
visited[x][y][s] = true;
add(x - 1, y, s, w, n, m, map, visited, heap);
add(x + 1, y, s, w, n, m, map, visited, heap);
add(x, y - 1, s, w, n, m, map, visited, heap);
add(x, y + 1, s, w, n, m, map, visited, heap);
}
return -1;
}
// 当前是由(a,b,s) -> (x,y,状态?)
// w ,从最开始到达(a,b,s)这个点的距离 -> w+1
// n,m 固定参数,防止越界
// map 地图
// visited 访问过的点,不要再加入到堆里去!
// heap, 堆!
public static void add(
int x, int y, int s,
int w, int n, int m,
char[][] map, boolean[][][] visited,
PriorityQueue<int[]> heap) {
if (x < 0 || x == n || y < 0 || y == m || map[x][y] == '#') {
return;
}
if (map[x][y] >= 'A' && map[x][y] <= 'Z') { // 锁!
// B -> 00000010
// dcba
// x,y,状 = x,y,s
// s == 00001000
// dcba
// A s & (1 << 0) != 0
// B s & (1 << 1) != 0
// D s & (1 << 3) != 0
//
if (!visited[x][y][s] && (s & (1 << (map[x][y] - 'A'))) != 0) {
heap.add(new int[] { x, y, s, w + 1 });
}
} else { // 不是锁!
// 要么是钥匙 a b c
// 要么是空房间 .
// 要么是初始位置 @
if (map[x][y] >= 'a' && map[x][y] <= 'z') {
s |= 1 << (map[x][y] - 'a');
}
if (!visited[x][y][s]) {
heap.add(new int[] { x, y, s, w + 1 });
}
}
}
}