南昌理工ACM集训队
克鲁斯卡尔(kruskal)算法是求连通网最小生成树的一种算法。它通过每次寻找权值最小边来求得最小生成树。
设有n个点,m条边。
- 将m条边进行排序(按权值从小到大排序)。
- 依次选择边,并判断边的两端节点是否在同一集合中。如果不在同一集合,连接两点,并将两点放入同一集合。如果在同一集合则不能连接(不然会形成回路)。
- 只到找到n-1条边。(n-1条边就可以让所有点全部连接到了)
具体步骤
- 找到最小权值**(2)**所在边
因为两端节点不在同一集合所以连接两点
- 接着找到**(3)**
连接后不会形成回路,两边都相连。
- 然后找到**(4)**
发现两边都连接后会形成回路,就只连一边,如果连4–6,树的最长路径为4。如果连2–5,树的最长路径为3。所以连2–5。
- 然后找到**(5)**
发现两端节点都在同一集合,所以都不连。
- 然后找到**(6)**
两端节点不在同一集合,连接
因为一共只有6个点,到这时已经找到5条边了,所以不用往下找了,已经是最小生成树了。
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 10 //边数
#define N 6 //点数
int fa[N],ran[N];
int t = 0;
struct Edge {
int s, e, v; //开始点(start),结束点(end),权值(value)
}side[INF];
int cmp(Edge x, Edge y) {
return x.v < y.v;
}
Edge edge[INF] = { //录入数据
{ 1, 2, 2 },
{ 1, 3, 7 },
{ 1, 4, 5 },
{ 2, 4, 3 },
{ 2, 5, 4 },
{ 3, 4, 6 },
{ 3, 6, 7 },
{ 4, 5, 5 },
{ 4, 6, 4 },
{ 5, 6, 3 } };
void init(){
for (int i = 1; i < N; i++) {
fa[i] = i; //初始化,每个节点的集合只有自己
ran[i] = 1; //初始化,每个节点的路径都为1
}
}
int fin(int x) { // 并查集,寻找每个节点的所在集合
if (fa[x] == x)
return x;
else return fa[x] = fin(fa[x]);
}
void uni(int i, int j) {
int x = fin(i), y = fin(j);
if (x == y) return;
if (ran[x] <= ran[y])
fa[x] = y;
else fa[y] = x;
if (ran[x] == ran[y] && x != y)
ran[y]++;
}
int kruskal(int a, int b, int c) {
int sum = 0;
if (fin(a) != fin(b)) { //如果两端点不在同一集合,连接两点
uni(a, b);
sum += c;
t++; //记录已加入的边的数量
}
return sum; //返回加入边的权值
}
int main()
{
init();
sort(edge, edge + INF, cmp); //cmp按权值排序
int ans = 0; // 记录最小生成树权值总和
for (int i = 0; i < INF; i++) {
ans += kruskal(edge[i].s, edge[i].e, edge[i].v);
if (t == N - 1) //N-1条边就构成最小生成树
break;
}
cout << ans << endl;
}