从(0, 0) 到 矩阵右下角 找出一条最短路径,
注意路径的cost不是数字的和,而是这条路径上每两个相邻点的差有个绝对值,路径的cost定义为这些差的绝对值中最大的。每个路径对应一个cost。
在所有路径中找出cost最小的。返回cost。
思路:
我们知道最短路径算法有Dijkstra算法,
dist[v]表示从起点(0, 0)到点v的距离,cost(u, v)表示点u到点v的距离。
如果途中经过u点,那么dist[v] = dist[u] + cost(u, v)
Dijkstra算法是这么个流程:
dist数组初始化为最大值。
从起点(0, 0)出发,计算从(0,0)点到与它相连的所有点的距离,
如果(0, 0)到点v的距离cost[v] < dist[v],那么更新dist[v] = cost[v]
并把(v, dist[v])装入优先队列。
下次从优先队列中取出dist最小的,再从取出的点出发,重复上面的过程,直到优先队列中没有点。
思想就是每次取出从起点到现在路径最短的点,以这个点为中转点,计算它到下一个点的距离,并更新最小距离。
当然上面的算法基于的距离是边的权重,那我们这里如何定义边的权重呢,
假如现在从u点出发,那么与它连接(有边)的点 v 就是它的上下左右四个点,
边的权重就是两个点的height差的绝对值,记为cost。
因为路径的COST是每步cost(差的绝对值)中最大的那个,所以只需要把cost和dist[u]相比,取较大的那个,
也就是cost[v] = max(dist[u], cost),
但是假如计算出来的cost[v]比之前保存的dist[v]大,也就是说当前不是最优路径,那么直接丢弃,不再保存到优先队列,也不更新dis[v]。
如果是最优路径,cost[v] < dist[v], 那么更新dist[v] = cost[v],
并把(v, dist[v])存入优先队列。
下次从优先队列中取出dist最小的,重复上面的过程,直到优先队列中不再有值。
*注意这里的cost是计算整个路径COST时其中的一步,而整个路径COST是所有这些cost中最大的。
计算每条路径COST时取cost最大,最后从众多路径中选择的时候取COST最小。
public int minimumEffortPath(int[][] heights) {
int[] move = new int[]{-1, 0, 1, 0, -1};
int rows = heights.length;
int cols = heights[0].length;
PriorityQueue<Pair<Integer, Integer>> queue = new PriorityQueue<>((a, b)->(a.getValue() - b.getValue()));
int[] dist = new int[rows * cols];
Arrays.fill(dist, Integer.MAX_VALUE); //起点(0,0)到当前点的距离
dist[0] = 0;
queue.offer(new Pair(0, 0));
while(!queue.isEmpty()) {
Pair<Integer, Integer> cur = queue.poll();
int idx = cur.getKey().intValue();
int cost = cur.getValue().intValue();
if(idx == rows * cols - 1) return cost;
if(cost > dist[idx]) continue;
int r = idx / cols;
int c = idx % cols;
for(int i = 0; i < 4; i++) {
int curR = r + move[i];
int curC = c + move[i+1];
if(curR < 0 || curC < 0 || curR >= rows || curC >= cols) continue;
int curIdx = curR * cols + curC;
int curCost = Math.abs(heights[curR][curC] - heights[r][c]);
if(Math.max(cost, curCost) >= dist[curIdx]) continue;
dist[curIdx] = Math.max(cost, curCost);
queue.offer(new Pair<Integer, Integer>(curIdx, dist[curIdx]));
}
}
return 0;
}