最小生成树之~普里姆算法
最小生成树是指从连好的图中(有n个点,多于(n-1)条边)选取n-1条边将n个点相互连接,并使得此树的总权值最小。完成此构造的方法著名的有两种,一个是kruskal算法,此算法是对每条边的权值进行排序,然后依次选取小的边添加到树上,并保证是一棵树(即不能产生回路)。另外一个就是prim算法,此算法是从点的角度来考虑。首先用map[][]二维数组存放两点间的权值,另外使用一个一维数组lowcost[]来存放与所选取的点相关联的权值,另外也使用一个mark[]数组用来标记已使用过的点。基本思想与最短路径中dijkstra算法十分相似。
prim算法基本思想:
从连通网络N={V,E},中选取一点S出发,选择与它相关联并且权值最小的边(S,V),将其顶点加入到生成树的顶点集合U中。以后每一步都选取一个不在顶点集合U中的点,使其到U的权值最小,然后将其加入到顶点集合U中。如此下去,直到网络中所有顶点均加入到生成树的顶点集合U中为止。
用prim算法构造最小生成树的过程:
假设在构造过程中,树的顶点集U与边集均为红色,U集合之外的点为蓝色,连接红与蓝的边为紫色,则 最短的紫色边就是当前要寻找的边。每选取一个顶点就需对剩下的顶点作调整,更新其到树的顶点集U的距离。具体请参考代码:
#include<stdio.h>
#include<string.h>
#define INF 0x3f3f3f3f
int map[1010][1010];
int lowcost[1010];
int mark[1010];
int n,m;
int prim()
{
int vir,min,sum=0;
for(int i=1;i<=n;i++) //将标记数组初始化为0,lowcost数组存放与起点1有关的点的权值
{
mark[i]=0;
lowcost[i]=map[1][i];
}
mark[1]=1; //将起点标记
for(int i=1;i<n;i++) //有n个点,所以需查找n-1次
{
min=INF;
for(int j=1;j<=n;j++) //查找距树集合权值最小的点
{
if(!mark[j]&&lowcost[j]<min)
{
min=lowcost[j];
vir=j;
}
}
if(min==INF) break;
sum+=min; //最小权值累加
mark[vir]=1; //标记已找到过的点
//更新各点距树集合的距离
for(int j=1;j<=n;j++)
{
if(!mark[j]&&lowcost[j]>map[vir][j])
{
lowcost[j]=map[vir][j];
}
}
}
return sum; //返回最小生成树的权值
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) //将map数组初始化为较大的值
map[i][j]=INF;
for(int i=0;i<m;i++)
{
int x,y,cost;
scanf("%d%d%d",&x,&y,&cost);
if(map[x][y]>cost) //记录点与权值,若有重复取权值小的
{
map[x][y]=cost;
map[y][x]=cost;
}
}
printf("%d\n",prim()); //输出此最小生成树的权值
}
return 0;
}