题目地址:
https://www.lintcode.com/problem/minimum-risk-path/description
给定 m m m条无向边,每条边给出两个端点和权值(权值都是正的)。一条路径的危险值定义为其所有边的权值的最大值。给定一个顶点 n n n,问从顶点 0 0 0到 n n n的所有路径中危险值最小的那个路径的危险值是多少。
思路是用最小生成树的Kruskal算法。先将边按照权值排序,然后一条一条地将边加上去,一旦发现 0 0 0和 n n n连通了之后,正在加的那条边的权值就是答案(判断连通需要用到并查集)。
算法正确性证明:
正确性还是蛮显然的。显然最小危险值不会大于算法求得的值,因为Kruskal算法已经算出了一条路径;而最小危险值也不会小于算法求得的值,因为只靠那些权值更小的边是无法构造出连通图的。所以算法求得的值即为所求。
代码如下:
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Solution {
class UnionFind {
private Map<Integer, Integer> parent;
public UnionFind() {
parent = new HashMap<>();
}
void add(int x) {
parent.putIfAbsent(x, x);
}
int find(int x) {
if (x != parent.get(x)) {
parent.put(x, find(parent.get(x)));
}
return parent.get(x);
}
// 如果x和y已经在一个集合里了就返回false,否则union它们所在集合并返回true
boolean union(int x, int y) {
int px = find(x), py = find(y);
if (px == py) {
return false;
}
parent.put(px, py);
return true;
}
}
class Edge {
// 把边的权值和它在原数组中的下标记录下来
int weight, idx;
public Edge(int weight, int idx) {
this.weight = weight;
this.idx = idx;
}
}
/**
* @param n: maximum index of position.
* @param m: the number of undirected edges.
* @param x:
* @param y:
* @param w:
* @return: return the minimum risk value.
*/
public int getMinRiskValue(int n, int m, int[] x, int[] y, int[] w) {
// Write your code here.
UnionFind uf = new UnionFind();
Edge[] edges = new Edge[m];
for (int i = 0; i < m; i++) {
edges[i] = new Edge(w[i], i);
uf.add(x[i]);
uf.add(y[i]);
}
// 按照权值排序
Arrays.sort(edges, (e1, e2) -> Integer.compare(e1.weight, e2.weight));
for (int i = 0; i < edges.length; i++) {
int idx = edges[i].idx;
// 把这条边加在图中
uf.union(x[idx], y[idx]);
// 一旦发现0和n连通了,就返回边的权值
if (uf.find(0) == uf.find(n)) {
return edges[i].weight;
}
}
return -1;
}
}
时间复杂度 O ( w log w ) O(w\log w) O(wlogw),空间 O ( V + w ) O(V+w) O(V+w)。