畅通工程
http://acm.hdu.edu.cn/showproblem.php?pid=1863
1.Prim算法:
Prim算法是由一个点(最初的集合)向外延伸,找到与集合相连权值最小的边,
然后把对应顶点拉到集合里,直到所有的顶点全部在集合里为止。
Prim算法的演示如下:
http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/flashhtml/prim.htm
#include <stdio.h> const int INF=9999999; const int MAXV=101; int map[MAXV][MAXV]; int res; void Prim(int map[MAXV][MAXV],int numV) { int i,j,k; int lowcost[MAXV]; lowcost[1]=0; //顶点1已经在集合里 for(i=2;i<=numV;i++) { lowcost[i]=map[1][i]; //lowcost[]里存储的是当前集合相连的所有边的权值 } for(i=2;i<=numV;i++) //依次循环除1以外的其余点,把它拉集合里 { int min=INF; k=1; //k初始化为1,是为了避免下一个循环里的k=j没执行,k为任意值,lowcost[k]数组溢出 for(j=2;j<=numV;j++) { if(lowcost[j]!=0&&lowcost[j]<min) //查找集合外与集合相连的点 { min=lowcost[j]; k=j; } } res+=min; lowcost[k]=0; //lowcost[k]=0表示顶点k被拉到集合里 for(j=2;j<=numV;j++) { if(lowcost[j]!=0&&map[k][j]<lowcost[j]) { lowcost[j]=map[k][j]; } //顶点k加入集合后,集合发生变化,更新与集合相连边的权值 } } } int main() { int n,m,i,j; int begin,end,w; while(scanf("%d%d",&n,&m)!=EOF&&n) { for(i=1;i<=m;i++) { for(j=1;j<=m;j++) { if(i==j) map[i][j]=0; if(i!=j) map[i][j]=INF; } //创建邻接矩阵 } for(i=1;i<=n;i++) { scanf("%d%d%d",&begin,&end,&w); map[begin][end]=w; map[end][begin]=w; } res=0; Prim(map,m); if(res>INF) //表示lowcost里还有INF,即图不连通 printf("?\n"); else printf("%d\n",res); } return 0; }
2.Kruskal算法
Kruskal算法是先将所有的边按权值从小到大排序,在依次加边,
直到加了(顶点数-1)条边。注意:加边时避免出现环。
#include <stdio.h> #include <stdlib.h> struct node { int begin; int end; int w; }; node edge[5000]; int res,count; int cmp(const void *p1,const void *p2) { return (*(node*)p1).w-(*(node*)p2).w; } int Find(int *parent,int f) { while(parent[f]!=f) f=parent[f]; return f; } //寻找根节点,有点类似于并查集 void Kruskal(int numV,int numE) { int n,m,i; int parent[101]; for(i=1;i<=numV;i++) parent[i]=i; for(i=0;i<numE;i++) { n=Find(parent,edge[i].begin); m=Find(parent,edge[i].end); if(n!=m) { parent[n]=m; count++; res+=edge[i].w; }//如果一条边的两个端点的的节点相同,加上这条边后就出现环 } } int main() { int n,m,i; while(scanf("%d%d",&n,&m)!=EOF&&n) { for(i=0;i<n;i++) { scanf("%d%d%d",&edge[i].begin,&edge[i].end,&edge[i].w); } qsort(edge,n,sizeof(edge[0]),cmp); res=count=0; Kruskal(m,n); if(count<m-1) //加入的边数<顶点数-1,说明图不连通 printf("?\n"); else printf("%d\n",res); } return 0; }
小结:Prim算法是基于顶点来加边,而Kruskal算法是基于边