链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4141
题意:
给出一个n(n≤100)结点的图,求苗条度(最大边减最小边的值)尽量小的生成树。
分析:
首先把边按权值从小到大排序。对于一个连续的边集区间[L,R],如果这些边使得n个点全部连通,
则一定存在一个苗条度不超过W[R]-W[L]的生成树(其中W[i]表示排序后第i条边的权值)。
从小到大枚举L,对于每个L,从小到大枚举R,同时用并查集将新进入[L,R]的边两端的点合并成一个集合,
与Kruskal算法一样。当所有点连通时停止枚举R,换下一个L(并且把R重置为L)继续枚举。
代码:
1 import java.io.*; 2 import java.util.*; 3 import static java.lang.Math.*; 4 import static java.util.Arrays.*; 5 6 public class Main { 7 Scanner cin = new Scanner(new BufferedInputStream(System.in)); 8 final int INF = 0x3f3f3f3f; 9 final int UP = 100 + 5; 10 int n, m, prev[] = new int[UP]; 11 Edge edge[]; 12 13 class Edge implements Comparable<Edge> { 14 int f, b, v; 15 16 @Override 17 public int compareTo(Edge that) { 18 return v - that.v; 19 } 20 } 21 22 int seek(int v) { 23 return prev[v] == v ? v : (prev[v] = seek(prev[v])); 24 } 25 26 int solve() { 27 int remain, ans = INF; 28 for(int L = 0; L < m; L++) { 29 remain = n - 1; // 剩余合并次数 30 for(int i = 0; i < UP; i++) prev[i] = i; // 初始化并查集 31 for(int R = L; R < m; R++) { 32 int pf = seek(edge[R].f), pb = seek(edge[R].b); 33 if(pf == pb) continue; 34 prev[pf] = pb; 35 if(--remain > 0) continue; 36 ans = min(ans, edge[R].v - edge[L].v); 37 break; 38 } 39 } 40 return ans == INF ? -1 : ans; 41 } 42 43 void MAIN() { 44 while(true) { 45 n = cin.nextInt(); 46 m = cin.nextInt(); 47 if(n == 0 && m == 0) break; 48 edge = new Edge[m]; 49 for(int i = 0; i < m; i++) edge[i] = new Edge(); 50 for(int i = 0; i < m; i++) { 51 edge[i].f = cin.nextInt(); 52 edge[i].b = cin.nextInt(); 53 edge[i].v = cin.nextInt(); 54 } 55 sort(edge); 56 System.out.println(solve()); 57 } 58 } 59 60 public static void main(String args[]) { new Main().MAIN(); } 61 }