假设有n个城市和m条道路,对应无向图中的点和边。每条路的过路费对应边的权值。镖局现在需要选择一些道路进行疏通,以便边距可以达到任意一个城镇,要求花费的银子越少越好。换句话说,镖局的要求就是用最少的边让图连通(任意两点之间可以互相到达),将多余的边去掉。
很显然,要想让有n个顶点的图连通,那么至少需要n-1条边。如果一个连通无向图不包含回路,那么就是一棵树,其实这里就是求一个图的最小生成树。这里我们仅讨论无向图的最小生成树。
既然要求让边的总权值最小,自然可以想到首先选择最短的边,然后选择次短的边……直到选择了n-1条边为止。这就需要先对所有的边按照权值大小进行从小到大的排序,然后从最小的开始选,依次选择每一条边,直到选择了n-1条边让整个图连通为止。中间添加每一条边时还需要判断这条边对应的两个顶点是否已经连通,如果已经连通则舍弃这条边,进行下一条边的判断。
判断两个顶点是否已经连通,可以使用深度优先搜索或者广度优先搜索,但这样效率很低。更好的选择是使用并查集,将所有顶点放入一个并查集中,判断两个顶点是否连通,仅需要判断两个顶点是否在同一个集合(即是否有共同的祖先)即可,这样时间复杂度仅为O(logN)。
这个算法名为Kruskal,总结如下:首先按照边的权值进行从小到大的排序,每次从剩余的边中选择权值较小且边的两个顶点不在同一个集合内的边(就是不会产生回路的边),加入到生成树中,直到加入了n-1条边为止,
作者:南方以北
来源:CSDN
原文:https://blog.csdn.net/qq_25800311/article/details/83540062
#include<stdio.h>
#include<stdlib.h>
int f[14];
struct node{
int u,v,w;
}ans[14];
void Init(int *a,int n)
{
for(int i=1;i<=n;i++)
a[i]=i;
}
int cmp(const void *a,const void *b)
{
struct node *c=(struct node *)a;
struct node *d=(struct node *)b;
if(c->w!=d->w)
return c->w>d->w;
}
int get_f(int x)
{
if(f[x]==x)
return x;
else
{
f[x]=get_f(f[x]);
return f[x];
}
}
int merge(int a,int b)
{
int t1=get_f(a);
int t2=get_f(b);
if(t1!=t2)
{
f[t2]=t1;
return 1;
}
return 0;
}
int main()
{
int n,m,i,cnt,sum;
scanf("%d%d",&n,&m);
cnt=0;sum=0;
Init(f,n);
for(i=0;i<m;i++)
scanf("%d%d%d",&ans[i].u,&ans[i].v,&ans[i].w);
qsort(&ans[0],m,sizeof(ans[0]),cmp);
for(i=0;i<m;i++)
{
if(merge(ans[i].u,ans[i].v))
{
cnt++;
sum+=ans[i].w;
}
if(cnt==n-1)
break;
}
if(i==m)
printf("impossible");
else
printf("%d",sum);
return 0;
}