PART1
1.实现:
将每条边按权值从小到大进行排序
(要用并查集维护)容边上两点不在同一个树,则合并
2.时间复杂度:
Kruskal 算法的时间复杂度由排序算法决定,若采用快排则时间复杂度为 O(ElogE)
总时间复杂度为 O(ElogE+V α(V)) 【α(V)近似看作常数】
3.特别优势:
1.prim只能用在连结成一个总集合的情况,如果是k个集合,那就只能用Kruskal
2.kruskal算法不需要建图,只需要用并查集
4.适用情况:
5.需要注意的点:
PART2
可用代码
#include<cstdio> #include<cmath> #include<algorithm> #include<set> #include<map> #include<cstring> #include<string> #include<vector> #include<queue> #include<iomanip> #include<iostream> using namespace std; const int N = 5e3+10; const int M = 2e5+10; #define inf 0x3f3f3f3f //kruskalEdge struct kruskalEdge { int u, v, w; } kE[M]; //MergeFindSet int fa[N]; int Get(int x) { if(fa[x] == x) return x; return fa[x] = Get(fa[x]); } inline void MergeFindSetInit(int &n) { for(int i = 1; i<= n; i++) fa[i] = i; } bool Merge(int u, int v) { int fu = Get(u); int fv = Get(v); if(fu != fv) { fa[fv] = fu; return true; } else return false; } //Kruskal bool KruskalCmp(kruskalEdge a, kruskalEdge b) { return a.w < b.w; } int Kruskal(int n, int m) { int sum = 0; MergeFindSetInit(n); sort(kE+1, kE+m+1, KruskalCmp); int cnt = 0;//记录合并了多少次 for(int i = 1; i <= m; i++) { if(Merge(kE[i].u, kE[i].v)) { cnt++; sum += kE[i].w; } } if(cnt == n-1) return sum; else return -1; } //in inline void KruskalIn(int &n, int &m) { cin >> n >> m; for(int i = 1; i <= m; i++) { cin >> kE[i].u >> kE[i].v >> kE[i].w; } } int main() { //freopen("in.txt","r", stdin); //freopen("out.txt","w", stdout); ios::sync_with_stdio(false); int n, m; KruskalIn(n, m); int ans = Kruskal(n, m); if(ans == -1) cout <<"no answer"; else cout << ans << endl; return 0; }
PART3
1.例题1:次小生成树
给定一个无向连通图,求出它的边权总和第二小的生成树。
解法:枚举最小生成树上的 n 条边,对于其中某条边,从图中删除它以后计算剩余图的最小生成树,一共 n−1棵。
从这 n−1 棵生成树中找出最小的一棵,就是整个图的次小生成树。
2.例题 2:边权极差最小生成树
给定一个无向连通图,求出它所有生成树中,最大边权和最小边权之差最小的生成树。
解法:利用例题 1 中给出的性质,枚举生成树上的最小边权 min_w,计算边权最小为 min_w的最小生成树,用当前最小生成树的最大边减去最小边来更新最终答案。
PART4
PART5