(带全无向连通)图的所有生成树中具有边上的权值之和最小的树称为图的最小生成树。
1.Prim算法
主要思想:Prim是先选出一个源点,将它放入已选集合中,从与已选集合中的点相连,且另一点没有被选中的权值最小的边,将这个点也放入已选集合中,重复之前的步骤。每一次循环都会找到一条边,所以循环进行verNum-1次。
//6 10 1 2 6 1 3 1 1 4 5 2 3 5 2 5 3 3 4 5 3 5 6 3 6 4 4 6 2 5 6 6
//9 14 1 2 4 1 8 8 2 3 8 2 8 11 3 4 7 3 6 4 3 9 2 4 5 9 4 6 14 5 6 10 6 7 2 7 8 1 7 9 6 8 9 7
#include<iostream>
using namespace std;
#define MAX_V 5005
#define MAX 0x7fffff
int verNum,edgeNum,sum=0;
int edge[MAX_V][MAX_V];
int pre[5005];//数组用于临时存储当前选中边的另一个端点,每次输出选中情况后更新为-1不在参与接下来的工作
void Prim()
{
int *dist=new int[verNum+1];//数组从1开始,防RE
for(int i=1;i<=verNum;i++)
{
dist[i]=edge[1][i];
pre[i]=1;
}
pre[1]=-1;//以1作为源点
for(int i=1;i<verNum;i++)
{
int temp=MAX;
int x=0;
for(int j=1;j<=verNum;j++)
{
if(pre[j]!=-1&&dist[j]<temp)//寻找未选中的距离最小的边
{
x=j;
temp=dist[j];
}
}
//cout<<pre[x]<<"->"<<x<<":"<<temp<<endl;
pre[x]=-1;
sum+=temp;
for(int i=2;i<=verNum;i++)
{
if(pre[i]!=-1&&edge[x][i]<dist[i])
{
dist[i]=edge[x][i];//区分于单源最短路径,dist数组表示的是该点到当前最小生成树的边的距离,不是到源点的距离
pre[i]=x;
}
}
}
}
int main()
{
cin>>verNum>>edgeNum;
for(int i=1;i<=verNum;i++)
for(int j=1;j<=i;j++)
edge[i][j]=edge[j][i]=MAX;
for(int i=1;i<=edgeNum;i++)
{
int a,b,c;
cin>>a>>b>>c;
edge[b][a]=edge[a][b]=min(c,edge[a][b]);//洛谷防重边
}
Prim();
bool flag=true;
for(int i=1;i<=verNum;i++)
if(pre[i]!=-1)
{
flag=false;
break;
}
if(flag==true)
cout<<sum<<endl;
else
cout<<"orz"<<endl;
return 0;
}
2.Kruskal算法
主要思想:Kruskal主要是利用了查并集的思想,先将边按从小到大排列,然后从权值小的开始,如果边的两个端点不在一个集合就可以输出,然后将它们合并到一个集合中。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
//6 10 1 2 6 1 3 1 1 4 5 2 3 5 2 5 3 3 4 5 3 5 6 3 6 4 4 6 2 5 6 6
//9 14 1 2 4 1 8 8 2 3 8 2 8 11 3 4 7 3 6 4 3 9 2 4 5 9 4 6 14 5 6 10 6 7 2 7 8 1 7 9 6 8 9 7
int vertexnum,edgenum;//点的总数和边的总数
int root[5005],ans=0;//记录每个点的上级
struct Graphic
{
int weight;//权重
int x;
int y;
}g[200005];
bool cmp(Graphic a,Graphic b)
{
return a.weight<b.weight;
}
int find(int x)//查找x的根
{
if(root[x]==x)
return x;
return root[x]=find(root[x]);
}
int merge(int x,int y)
{
int rx=find(x),ry=find(y);
if(rx==ry)
return 0;
root[rx]=ry;
return 1;
}
void kruskal()
{
int i,j;
for(int k=1;k<=edgenum;k++)
{
i=g[k].x;
j=g[k].y;
if(merge(i,j))//如果没有在一个集合内
ans+=g[k].weight;
cout<<i<<" -> "<<j<<" : "<<g[k].weight<<endl;
}
}
int main()
{
cin>>vertexnum>>edgenum;
for(int i=1;i<=vertexnum;i++)
root[i]=i;
for(int i=1;i<=edgenum;i++)
cin>>g[i].x>>g[i].y>>g[i].weight;
sort(g,g+edgenum+1,cmp);//按权重从小到大排序
kruskal();
int i;
for(i=1;i<vertexnum;i++)
if(root[i]!=root[i+1])
{
cout<<"orz"<<endl;
break;
}
if(i==vertexnum)
cout<<ans<<endl;
return 0;
}