package class44; import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; public class Problem_0317_ShortestDistanceFromAllBuildings { // 如果grid中0比较少,用这个方法比较好 public static int shortestDistance1(int[][] grid) { int ans = Integer.MAX_VALUE; int N = grid.length; int M = grid[0].length; int buildings = 0; Position[][] positions = new Position[N][M]; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (grid[i][j] == 1) { buildings++; } positions[i][j] = new Position(i, j, grid[i][j]); } } if (buildings == 0) { return 0; } for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { ans = Math.min(ans, bfs(positions, buildings, i, j)); } } return ans == Integer.MAX_VALUE ? -1 : ans; } public static int bfs(Position[][] positions, int buildings, int i, int j) { if (positions[i][j].v != 0) { return Integer.MAX_VALUE; } HashMap levels = new HashMap<>(); Queue queue = new LinkedList<>(); Position from = positions[i][j]; levels.put(from, 0); queue.add(from); int ans = 0; int solved = 0; while (!queue.isEmpty() && solved != buildings) { Position cur = queue.poll(); int level = levels.get(cur); if (cur.v == 1) { ans += level; solved++; } else { add(queue, levels, positions, cur.r - 1, cur.c, level + 1); add(queue, levels, positions, cur.r + 1, cur.c, level + 1); add(queue, levels, positions, cur.r, cur.c - 1, level + 1); add(queue, levels, positions, cur.r, cur.c + 1, level + 1); } } return solved == buildings ? ans : Integer.MAX_VALUE; } public static class Position { public int r; public int c; public int v; public Position(int row, int col, int value) { r = row; c = col; v = value; } } public static void add(Queue q, HashMap l, Position[][] p, int i, int j, int level) { if (i >= 0 && i < p.length && j >= 0 && j < p[0].length && p[i][j].v != 2 && !l.containsKey(p[i][j])) { l.put(p[i][j], level); q.add(p[i][j]); } } // 如果grid中1比较少,用这个方法比较好 public static int shortestDistance2(int[][] grid) { int N = grid.length; int M = grid[0].length; int ones = 0; int zeros = 0; Info[][] infos = new Info[N][M]; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (grid[i][j] == 1) { infos[i][j] = new Info(i, j, 1, ones++); } else if (grid[i][j] == 0) { infos[i][j] = new Info(i, j, 0, zeros++); } else { infos[i][j] = new Info(i, j, 2, Integer.MAX_VALUE); } } } if (ones == 0) { return 0; } int[][] distance = new int[ones][zeros]; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (infos[i][j].v == 1) { bfs(infos, i, j, distance); } } } int ans = Integer.MAX_VALUE; for (int i = 0; i < zeros; i++) { int sum = 0; for (int j = 0; j < ones; j++) { if (distance[j][i] == 0) { sum = Integer.MAX_VALUE; break; } else { sum += distance[j][i]; } } ans = Math.min(ans, sum); } return ans == Integer.MAX_VALUE ? -1 : ans; } public static class Info { public int r; public int c; public int v; public int t; public Info(int row, int col, int value, int th) { r = row; c = col; v = value; t = th; } } public static void bfs(Info[][] infos, int i, int j, int[][] distance) { HashMap levels = new HashMap<>(); Queue queue = new LinkedList<>(); Info from = infos[i][j]; add(queue, levels, infos, from.r - 1, from.c, 1); add(queue, levels, infos, from.r + 1, from.c, 1); add(queue, levels, infos, from.r, from.c - 1, 1); add(queue, levels, infos, from.r, from.c + 1, 1); while (!queue.isEmpty()) { Info cur = queue.poll(); int level = levels.get(cur); distance[from.t][cur.t] = level; add(queue, levels, infos, cur.r - 1, cur.c, level + 1); add(queue, levels, infos, cur.r + 1, cur.c, level + 1); add(queue, levels, infos, cur.r, cur.c - 1, level + 1); add(queue, levels, infos, cur.r, cur.c + 1, level + 1); } } public static void add(Queue q, HashMap l, Info[][] infos, int i, int j, int level) { if (i >= 0 && i < infos.length && j >= 0 && j < infos[0].length && infos[i][j].v == 0 && !l.containsKey(infos[i][j])) { l.put(infos[i][j], level); q.add(infos[i][j]); } } // 方法三的大流程和方法二完全一样,从每一个1出发,而不从0出发 // 运行时间快主要是因为常数优化,以下是优化点: // 1) 宽度优先遍历时,一次解决一层,不是一个一个遍历: // int size = que.size(); // level++; // for (int k = 0; k < size; k++) { ... } // 2) pass的值每次减1何用?只有之前所有的1都到达的0,才有必要继续尝试的意思 // 也就是说,如果某个1,自我封闭,之前的1根本到不了现在这个1附近的0,就没必要继续尝试了 // if (nextr >= 0 && nextr < grid.length // && nextc >= 0 && nextc < grid[0].length // && grid[nextr][nextc] == pass) // 3) int[] trans = { 0, 1, 0, -1, 0 }; 的作用是迅速算出上、下、左、右 // 4) 如果某个1在计算时,它周围已经没有pass值了,可以提前宣告1之间是不连通的 // step = bfs(grid, dist, i, j, pass--, trans); // if (step == Integer.MAX_VALUE) { // return -1; // } // 5) 最要的优化,每个1到某个0的距离是逐渐叠加的,每个1给所有的0叠一次(宽度优先遍历) // dist[nextr][nextc] += level; public static int shortestDistance3(int[][] grid) { int[][] dist = new int[grid.length][grid[0].length]; int pass = 0; int step = Integer.MAX_VALUE; int[] trans = { 0, 1, 0, -1, 0 }; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == 1) { step = bfs(grid, dist, i, j, pass--, trans); if (step == Integer.MAX_VALUE) { return -1; } } } } return step == Integer.MAX_VALUE ? -1 : step; } // 原始矩阵是grid,但是所有的路(0),被改了 // 改成了啥?改成认为,pass才是路!原始矩阵中的1和2呢?不变! // dist,距离压缩表,之前的bfs,也就是之前每个1,走到某个0,总距离和都在dist里 // row,col 宽度优先遍历的,出发点! // trans -> 炫技的,上下左右 // 返回值代表,进行完这一遍bfs,压缩距离表中(dist),最小值是谁? // 如果突然发现,无法联通!返回系统最大! public static int bfs(int[][] grid, int[][] dist, int row, int col, int pass, int[] trans) { Queue que = new LinkedList(); que.offer(new int[] { row, col }); int level = 0; int ans = Integer.MAX_VALUE; while (!que.isEmpty()) { int size = que.size(); level++; for (int k = 0; k < size; k++) { int[] node = que.poll(); for (int i = 1; i < trans.length; i++) { // 上下左右 int nextr = node[0] + trans[i - 1]; int nextc = node[1] + trans[i]; if (nextr >= 0 && nextr < grid.length && nextc >= 0 && nextc < grid[0].length && grid[nextr][nextc] == pass) { que.offer(new int[] { nextr, nextc }); dist[nextr][nextc] += level; ans = Math.min(ans, dist[nextr][nextc]); grid[nextr][nextc]--; } } } } return ans; } }