算法一:Prim算法(可称为加点法)
点集记为v。 定义一个集合U,来存储已经在生成树中的点。v-u是未在生成树中的点。定义一个closedge数组,分为两个区域,一个存储最小边在u中的顶点,另一个存储最小边的权值。将初始顶点加入U中,初始化closedge数组。选择最小边closedge[k],将K加入集合U,输出此边。更新closedge[]的值,选择集合V-U中点到k和u之间较小的哪个权值存到clodedge数组中。重复此步骤,直到所有的顶点都加入u中。
用邻接矩阵存储图,用一维数组存储现已有的树到其他定点的最小权值,用另一个一维数组存储某个点是否在生成树中。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define Maxn 110
#define INF 9999999
int maz[Maxn][Maxn],lowcase[Maxn],flag[Maxn];
//maz[i][j]表示i到j的距离,注意无向图和重边!lowcase数组存的是现已在树里的点所能到达的点(Maxn)的最小权值
//flag数组是用来标记某个点(Maxn)是否已经在树里面
int prim(int n)
{
int i,j,ans = 0,pos,mi;
memset(flag,0,sizeof(flag));//起初所有点都不在生成树中
for (i = 2; i <= n; ++i)
lowcase[i] = maz[1][i];//先以1作为根节点,更新lowcase数组
lowcase[1] = 0;//为了数组里的值全(自己到自己的距离是0)
flag[1] = 1;//1这个点(初始点)标记为已在树里面
for (i = 0; i < n-1; ++i)//循环n-1次,每次找一个点纳入到树里面,加上1根节点总共就是n个点
{
mi = INF;//mi为每次在lowcase数组里找到的最小值,所以刚开始赋值成最大值
for (j = 1; j <= n; ++j)//寻找最小权值的点
{
if (!flag[j] && lowcase[j] < mi)//j点不在树里面并且权值小
{
mi = lowcase[j];
pos = j;//记录一下这个将要进入生成树的点
}
}
ans += mi;//记录最小生成树的总权值
flag[pos] = 1;//将此次寻找的点标记为已经加入生成树
for (j = 1; j <= n; ++j)//用新的节点更新lowcase数组
{
if (!flag[j] && maz[pos][j] < lowcase[j])//判断新加入的点到其他所有未加入的点的距离是否比前一个点的小
{
lowcase[j] = maz[pos][j];//如果小的话更新数组的值
}
}
}
return ans;//返回最小生成树的总权值
}
int main()
{
int n,i,j;
while (~scanf("%d",&n) && n)//顶点个数
{
for (i = 1; i <= n; ++i)
{
for (j = 1; j <= n; ++j)
maz[i][j] = i == j ? 0 : INF;//用邻接矩阵存储图
}//刚开始得把数组里的数存成无穷大//本身到本身的距离是0//点和点之间的距离是无穷大
int a,b,c;
for (i = 0; i < n*(n-1)/2; ++i)//最大边数
{
scanf("%d%d%d",&a,&b,&c);
if (c < maz[a][b])
maz[a][b] = maz[b][a] = c;//无向图+重边
}
int ans = prim(n);//n个点的最小生成树的总长度
printf("%d\n",ans);
}
return 0;
}
算法二:克鲁斯卡尔(加边法)
将所有边的权值排序。
1)初始状态看为只有顶点没有边,每一个顶点看作一个连通分量。
2)选择最小权值的边,如果该边连接了不同连通分量,则将该边加入集合T,否则舍去该边,继续寻找下一条权值最小的边。
3)重复2)直到所有顶点都在同一个连通分量中。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define Maxn 1100
int p[Maxn];
int find(int x)
{
return p[x] == x ? x : p[x] = find(p[x]);
}//并查集
struct node
{
int u,v,w;
};//每条边的情况,u,v是边的端点,w是边的权值
struct node q[Maxn*500]; //定义边的个数
int cmp(node a,node b)
{
return a.w < b.w;
}//sort将边按权值排序
int main()
{
int n,m,sum,num,i,x,y;
while (~scanf("%d",&n) && n)
{
for (i = 1; i <= n; ++i)
p[i] = i;//初始化每个顶点是一个连通分量
m = n * (n-1) / 2; //最大边的数
for (i = 0; i < n*(n-1)/2; ++i)
{
scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].w);//****不需要考虑重边的情况,因为重边的存在并不覆盖
//之前的边,重边参与排序,选出重边中最小的,就算遍历到了重边中较大的,此时端点已经在同
//一棵树中了,所以重边的存在不会有影响
}
sort(q,q+m,cmp);//将边按照权值排序
sum = num = 0;
for (i = 0; i < m; ++i)//m条边,从其中选出****n-1条边,然后跳出循环
{
x = find(q[i].u);
y = find(q[i].v); //判断两个顶点的边是否在同一个连通分量中
if (x != y) //不在同一个连通分量中
{
if (x > y)
p[x] = y;
else
p[y] = x;//连通分量标志合并到哪个较小的中去
sum += q[i].w; //记录生成树的总权值
++num; //记录加入生成树中的边数
}
if (num == n-1) //当足够了n-1条边的时候停止
break;
}
printf("%d\n",sum); //输出总权值
}
return 0;
}