题目:
给出编号为1,2,......,N的N(N<100)个点两两间的距离,求该图的最小生成树的边和。
思路:
求最小生成树,用Kruskal算法。
知识点回顾:
1.Kruskal算法:初始时所有点孤立,属于不同的点集。
把边按权值从小到大排序,然后遍历边。
若边的两点属于不同点集,则该边为最小生成树的一部分,并把两点所在集合合并。
遍历完所有边,若所有点在同一点集,则找到最小生成树;否则原图不连通,没有最小生成树。
过程:
这次调试用了很久很久,出错的地方竟然在不起眼的cmp()函数上,即如果要从小到大排,要用“<”,不要用“<=”(从大到小同理),具体原因还未知(问题只出在九度OJ上,自己的编译器没问题)。
1 #include <iostream> 2 #include <stdio.h> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 //并查集 8 int tree[102]; //存点的父结点(根节点的值为0) 9 int findRoot(int i){ //返回点所在父结点,并压缩树 10 int j=i,k=i,t; 11 while(tree[j]!=0){ //找到i结点的根结点j 12 j=tree[j]; 13 } 14 while(tree[k]!=0){ //压缩树 15 t=k; 16 k=tree[k]; 17 tree[t]=j; 18 } 19 return j; 20 } 21 22 //边 23 struct Edge{ 24 int a; //a->b 25 int b; 26 int value; //边的权值 27 }; 28 29 Edge edge[5000]; //存边 30 31 //边比较函数 32 bool cmp(Edge e1,Edge e2){ 33 if(e1.value<e2.value) 34 return true; 35 return false; 36 } 37 38 //主函数 39 int main(){ 40 int N,i,j,sum; 41 int a,b,value,r1,r2; 42 while(scanf("%d",&N)!=EOF && N){ //输入点数 43 memset(tree,0,102*sizeof(int)); //把点各自分为独立集合 44 sum=0; 45 j=N*(N-1)/2; //计算边数 46 for(i=0;i<j;i++){ //循环输入边 47 scanf("%d %d %d",&edge[i].a,&edge[i].b,&edge[i].value); 48 } 49 sort(edge,edge+j,cmp); //把边按权值从小到大排序 50 for(i=0;i<j;i++){ //遍历边 51 a=edge[i].a; b=edge[i].b; value=edge[i].value; //边的信息 52 r1=findRoot(a); r2=findRoot(b); //找到根 53 if(r1!=r2){ //两点不在同集合,则该边为最小生成树的一部分 54 sum+=value; //把该边的权值加入和 55 tree[r1]=r2; //合并两集合 56 } 57 } 58 printf("%d\n",sum); 59 } 60 return 0; 61 }