题目链接:https://leetcode-cn.com/problems/path-with-minimum-effort/
一开始认为是动态规划,但无法初始化,不满足无后效性,排除。贪心也是不可能的,排除。
想到了最短路解法,dijkstra,来更新dijs数组,使得其最大值最小,然后超时了,过了一半的样例,需要采取更高效的算法。
import java.util.Arrays; class Solution { //节点数 int n; int[][] map; int dijkstra(int st, int ed) { int[] dij = new int[n]; boolean[] vis = new boolean[n]; for (int i = 0; i < n; i++) { dij[i] = map[st][i]; vis[i] = false; } vis[st] = true; //最多需要确定n - 1次,因为起点已经确定了 for (int i = 0; i < n - 1; i++) { if (vis[ed]) break; int minn = 0x3f3f3f3f; int v = -1; for (int j = 0; j < n; j++) { if (dij[j] < minn && !vis[j]) { minn = dij[j]; v = j; } } vis[v] = true; //松弛其他点 for (int j = 0; j < n; j++) { if (!vis[j]) { dij[j] = Math.min(dij[j], Math.max(dij[v], map[v][j])); } } } return dij[ed]; } public int minimumEffortPath(int[][] heights) { int row = heights.length; int col = heights[0].length; n = row * col; map = new int[n][n]; //二维数组map初始化 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) map[i][j] = 0; else map[i][j] = map[j][i] = 0x3f3f3f3f; } } //map数组存相邻节点边权(双向边) for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { int currentNode = i * col + j; int rightNode = currentNode + 1; int downNode = (i + 1) * col + j; if (j != col - 1) map[currentNode][rightNode] = map[rightNode][currentNode] = Math.abs(heights[i][j] - heights[i][j + 1]); if (i != row - 1) map[currentNode][downNode] = map[downNode][currentNode] = Math.abs(heights[i][j] - heights[i + 1][j]); /*if (rightNode < n) System.out.println("节点" + currentNode + "到节点" + rightNode + "的边权为:" + map[currentNode][rightNode]); if (downNode < n) System.out.println("节点" + currentNode + "到节点" + downNode + "的边权为:" + map[currentNode][downNode]);*/ } } return dijkstra(0, n - 1); } }
采用优先队列优化,快速得到当前最小的dij值即可
import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; class Solution { public int minimumEffortPath(int[][] heights) { //方向数组:上下左右 int[] dx = new int[]{0, 0, -1, 1}; int[] dy = new int[]{1, -1, 0, 0}; int rowNums = heights.length; int colNums = heights[0].length; int n = rowNums * colNums; int[] dij = new int[n]; Arrays.fill(dij, Integer.MAX_VALUE); dij[0] = 0; boolean[] vis = new boolean[n]; Arrays.fill(vis, false); //优先队列(从小到大) PriorityQueue<int[]> Q = new PriorityQueue<>(new Comparator<int[]>() { public int compare(int[] pre, int[] next) { return pre[2] - next[2]; } }); //所在row下标,所在col下标,dij[x * colNums + y]值 Q.add(new int[]{0, 0, 0}); while (!Q.isEmpty()) { int[] currentNode = Q.poll(); int x = currentNode[0]; int y = currentNode[1]; int currentId = x * colNums + y; vis[currentId] = true; if (vis[n - 1]) break; for (int k = 0; k < 4; k++) { int xx = x + dx[k]; int yy = y + dy[k]; int nextId = xx * colNums + yy; if (xx >= 0 && xx < rowNums && yy >= 0 && yy < colNums && !vis[nextId]) { if (Math.max(currentNode[2], Math.abs(heights[xx][yy] - heights[x][y])) < dij[nextId]) { dij[nextId] = Math.max(currentNode[2], Math.abs(heights[xx][yy] - heights[x][y])); Q.add(new int[]{xx, yy, dij[nextId]}); } } } } return dij[n - 1]; } }
献上属于这道题的草稿纸,手动跑朴素dijsktra的过程。通过这个过程发现了
1.方法一寻找当前最小dij值时的整个循环可以优化成方法二优先队列每次直接弹出最小dij
2.方法一松弛时对所有点遍历可以优化成方法二方向数组控制的几个邻接点的松弛