kruskal算法是最小生成树的常见算法,与并查集结合使用会更加高效,这篇博文主要介绍并查集做法。
kruskal算法主要思想如下:读入一个n点m边的图,对于每一条边,用一个结构体存储这条边连接的两个点及其边权。随后根据边权从小到大进行排序。一开始,我们先认为这n个点分属于不同的集合,即每个点自己是一个集合。(并查集初始化:自己的父亲是自己),接着,开始处理已经排好序的m条边。如果当前边的两个点分属于不同的集合,我们就把它们合并,将边权加到最终答案中,并且累加器加一(累加器用来判断当前已加入最小生成树的边数,若其为n-1,则已完成算法,退出循环)。
由于每次取的都是当前边权最小的边,所以可以保证最后求出的一定是最小生成树。具体过程详见代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2147483647;
struct edge
{
int u,v,w;
}a[200005];
int f[5005],n,m,ans,sum,k;
int find(int x)
{
if (f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
bool merge(int t1,int t2)
{
int r1=find(t1);
int r2=find(t2);
if (r1!=r2)
{
f[r1]=r2;
return true;
}
return false;
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int main()
{
int i,j;
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+m+1,cmp);
for (i=1;i<=n;i++)
f[i]=i;
for (i=1;i<=m;i++)
{
if (merge(a[i].u,a[i].v))
{
sum++;
ans+=a[i].w;
}
if (sum==n-1)
break;
}
for (i=1;i<=n;i++)
if (f[i]==i)
k++;
if (k==1)
printf("%d",ans);
else
printf("orz");
return 0;
}
此为洛谷P3366最小生成树模板题代码。