最小生成树:在 无向带权图 中选择一些边,在保证连通性的情况下,边的总权值最小
最小生成树可能不只一棵,只要保证边的总权值最小,就是正确的最小生成树。
如果无向带权图有n个点,那么最小生成树一定有n-1条边
Kruskal算法(最常用)
1 把所有的边,根据权值从小到大排序,从权值小的边开始考虑
2 如果连接当前的边不会形成环,就选择
3 如果连接当前的边会形成环,就不选
4 考察完所有的边,就得到最小生成树
用并查集来判断会不会形成环
P3366 【模板】最小生成树
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。
输入格式
第一行包含两个整数 $N,M$,表示该图共有 $N$ 个结点和 $M$ 条无向边。
接下来 $M$ 行每行包含三个整数 $X_i,Y_i,Z_i$,表示有一条长度为 $Z_i$ 的无向边连接结点 $X_i,Y_i$。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。
输入输出样例 #1
输入 #1
1 2 3 4 5 6
| 4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3
|
输出 #1
说明/提示
数据规模:
对于 $20%$ 的数据,$N\le 5$,$M\le 20$。
对于 $40%$ 的数据,$N\le 50$,$M\le 2500$。
对于 $70%$ 的数据,$N\le 500$,$M\le 10^4$。
对于 $100%$ 的数据:$1\le N\le 5000$,$1\le M\le 2\times 10^5$,$1\le Z_i \le 10^4$。
样例解释:

所以最小生成树的总边权为 $2+2+3=7$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StreamTokenizer; import java.util.Arrays;
public class Main {
public static int MAXN = 5001;
public static int MAXM = 200001;
public static int[] father = new int[MAXN];
public static int[][] edges = new int[MAXM][3];
public static int n, m;
public static void build() { for (int i = 1; i <= n; i++) { father[i] = i; } }
public static int find(int i) { if (i != father[i]) { father[i] = find(father[i]); } return father[i]; }
public static boolean union(int x, int y) { int fx = find(x); int fy = find(y); if (fx != fy) { father[fx] = fy; return true; } else { return false; } }
public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StreamTokenizer in = new StreamTokenizer(br); PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); while (in.nextToken() != StreamTokenizer.TT_EOF) { n = (int) in.nval; in.nextToken(); m = (int) in.nval; build(); for (int i = 0; i < m; i++) { in.nextToken(); edges[i][0] = (int) in.nval; in.nextToken(); edges[i][1] = (int) in.nval; in.nextToken(); edges[i][2] = (int) in.nval; } Arrays.sort(edges, 0, m, (a, b) -> a[2] - b[2]); int ans = 0; int edgeCnt = 0; for (int[] edge : edges) { if (union(edge[0], edge[1])) { edgeCnt++; ans += edge[2]; } } out.println(edgeCnt == n - 1 ? ans : "orz"); } out.flush(); out.close(); br.close(); }
}
|
时间复杂度O(m*logm)+O(n)+O(m)
Prim算法(不常用)