// 克鲁斯卡尔算法 // 计算任意两点间的距离,并记录每条边,编号【0 - n-1】 // 对于边的集合进行排序,从小到大 // (克鲁斯卡尔算法)依次遍历所有边,使用并查集判断是否处于同一集合,不属于一个集合就记录该边 // 当连通分量=1时,输出 #include <iostream> #include <vector> #include <cmath> #include <algorithm> using namespace std; struct Dot { int id; // 编号,re0 double x; double y; }; struct Edge { int from; int to; double length; }; vector<Dot> dot; vector<Edge> edge; // 并查集 start const int maxn = 100; int father[maxn]; int height[maxn]; void init(int n) { for (int i = 0; i < n; i++) { father[i] = i; height[i] = 0; } } int find(int x) { if (x != father[x]) { father[x] = find(father[x]); } return father[x]; } void Union(int x, int y) { x = find(x); y = find(y); if (x != y) { if (height[x] < height[y]) { father[x] = y; } else if (height[x] > height[y]) { father[y] = x; } else { father[y] = x; height[x]++; } } } int countFather(int n) { int count = 0; for (int i = 0; i < n; i++) { if (find(i) == i) count++; } return count; } // 并查集 end bool comp(Edge x,Edge y){ return x.length<y.length; } int n,num_of_edge;//结点数 边数 int main(){ while (cin>>n) { if(n==0) break; double x,y; for(int i=0;i<n;i++){ cin>>x>>y; Dot d; d.id=i; //进入vector顺序即编号,可不用id d.x=x; d.y=y; dot.push_back(d); }//处理输入 //每两个结点构成一条边 num_of_edge=n*(n-1)/2; for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ Edge e; e.from=i; e.to=j; e.length=sqrt((dot[i].x-dot[j].x)*(dot[i].x-dot[j].x)+(dot[i].y-dot[j].y)*(dot[i].y-dot[j].y)); edge.push_back(e); } } //对边从小到大排序 sort(edge.begin(),edge.end(),comp); init(n); double ans=0; for(int i=0;i<edge.size();i++){ if(find(edge[i].from) != find(edge[i].to)){ Union(edge[i].from , edge[i].to); ans+=edge[i].length; } if(countFather(n)==1){ break; } }//kruskal算法 printf("%.2f",ans); } }