下面例子使用的图。
/*
*克鲁斯卡尔算法
*步骤
1:给所有的边按照权值从小到大排列。
2:情况1:u和v在同一个连通分量中,那么加入(u,v)之后就会形成环了,因此不能选择。
情况2:u和v不同一个连通分量中,那个加入一定是最优的。
原因是:用反证法
如果不加这条边能得到一个最优解T,则T + (u,v)一定有且只有一个环,而且环中至少有一条边
(u',v')的权值大于或者等于(u,v)的权值,删除该边后,得到的新树T'不会比T更差,因此加入(u,v)
不会比不加入差
*/
#include "stdio.h"
#include "stdlib.h"
#define MAX 21
int v[MAX];
int u[MAX];
int w[MAX];
int set[MAX];
int r[MAX];
int compare (int i,int j) {
return w[i] - w[j];
}
//插入排序
void sort (int num[],int n,int (*compare)()) {
int i,j,key;
for (i = 1;i < n;i++) {
key = num[i];
j = i - 1;
while (j >= 0 && compare(num[j],key) >= 0) {
num[j+1] = num[j];
j--;
}
num[j+1] = key;
}
}
//并查集的find
int find (int i) {
if (set[i] == i) {
return i;
}else {
return find (set[i]);
}
}
//Kruskal算法
//n:顶点的个数,m:边的个数
//v[i],u[i],w[i]分别表示第i条边的关联的顶点的序号和权值。
//排序后的结果存放在r[i]中。这个并没有对v[i],u[i],w[i]对象进行排序,而是对r[i]排序,排序后的第i小的边存放在r[i]中,这就是间接排序-
//排序的关键字是对象的”代号“,而不是对象本身。
int kruskal (int n,int m) {
int result = 0;
int i;
//初始化并查集
for (i = 0;i < n;i++) {
set[i] = i;
}
//初始化边序号
for (i = 0;i < m;i++) {
r[i] = i;
}
sort (r,m,compare);//对边间接排序
for (i = 0;i < m;i++) {
int tmp = r[i];
int t1 = find (u[tmp]);
int t2 = find (v[tmp]);
if (t1 != t2) {
result += w[tmp];
set[t1] = t2;
}
}
return result;
}
/**
*input:
6 10
2 1 6
3 1 1
3 2 5
4 1 5
5 2 3
5 3 6
6 3 4
6 4 2
6 5 6
4 3 5
*result:
15
*/
int main () {
int n,m;
int i,_v,_u,_w;
int result;
scanf ("%d%d",&n,&m);
for (i = 0;i < m;i++) {
scanf ("%d%d%d",&_u,&_v,&_w);
u[i] = _u;
v[i] = _v;
w[i] = _w;
}
for (i = 0;i < m;i++) {
printf ("%d-%d:%d\n",u[i],v[i],w[i]);
}
result = kruskal (n,m);
printf ("%d\n",result);
return 0;
}
/*
Union-Find-Set(并查集)
并查集是用树来表示集合。
eg:包含1,2,3,4,5,6图的3个连通分量{1,3},{2,5,6},{4}3颗树来表示,
这3棵树是什么形态是无关紧要的,只要一棵树包含1,3二个点,一棵包含
2,5,6这3个点,一棵包含4这个点,。并规定每棵树的根结点是这棵树所
对应的集合的代表元。
如果把x的父结点保存在p[x]中(如果x没有父亲,则用p[x]=x)。
*/