最小生成树之prim算法
最小生成树的概念在上一篇文章中已经叙述了,在这里主要再叙述一种解决最小生成树的算法--prim算法。
他其实和kruskal算法是互补的,一个从边出发,另一个从点出发寻找最小的总权值。prim算法是从点入手,一开始随机选一个点,找出该点连接的其他所有点中最短的路径,然后把这条边的另一个点加入集合。当集合中的点大于等于2的时候,我们每次都要找出这个集合中与其他不在集合里的点连接的最短的那条边,然后再把这条边的不在集合里的那个点加入集合,就这样一直循环下去,知道把所有的点都加入到这个集合为止。这样每次记录边的权值,最后就是我们要找的答案。
为了更容易理解,还是以讲解kruskal算法的题目作为例题,示例代码如下(必要的解释有相应注释说明):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
using namespace std;
#define INF 0x3f3f3f3f
int n,m;
int map[105][105],low[105];//low数组表示整个集合对没有在集合的点的最小距离
bool vis[105];//用来表示第i个点是否被用过
void prim()
{
int pos,sum=0,MIN;
pos=1;//表示取第一个点作为集合里的第一个点,以这个点为原始点开始向集合里加点
vis[1]=1;
for(int i=1;i<=n;i++)
{
low[i]=map[pos][i];
}
//再循环n-1次就行了,每一次都加入一个点
for(int i=1;i<n;i++)
{
MIN=INF;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&(low[j]!=-1)&&MIN>low[j])
{
pos=j;MIN=low[j];
}
}
sum+=low[pos];
vis[pos]=1;
for(int j=1;j<=n;j++)//更新最小值
{
if(!vis[j]&&low[j]>map[pos][j]&&map[pos][j]!=-1&&low[j]!=-1)
low[j]=map[pos][j];
}
}
printf("%d\n",sum);
}
int main()
{
// freopen("s","r",stdin);
while(scanf("%d",&n)!=EOF)
{
memset(vis,0,sizeof(vis));
memset(map,-1,sizeof(map));
if(!n) {break;}
m=n*(n-1)/2;
for(int i=1;i<=m;i++)
{
int x1,x2,dis;
scanf("%d%d%d",&x1,&x2,&dis);
//表示x1和x2之间的边是多长
map[x1][x2]=dis;
map[x2][x1]=dis;
}
prim();
}
return 0;
}