//考点最小生成树
//用到并查集和Kruskal算法
//注意输出格式要求
#include <ios>
#include <iostream>
#include<vector>
#include<algorithm>
#include<math.h>
using namespace std;
//最小生成树
//并查集
int father[1000];
void InitDisjointSet(int n){
for(int i=0;i<n;i++){
father[i]=i;
}
}
int FindrootDisjointSet(int n){
if(father[n]==n){
return father[n];
}
else{
father[n]=FindrootDisjointSet(father[n]);
return father[n];
}
}
void UnionDisjointSet( int u,int v){
int u_root=FindrootDisjointSet(u);
int v_root=FindrootDisjointSet(v);
father[v_root]=u_root;
}
struct vertex{//顶点
float x;
float y;
};
struct edge{//边
int vertex1;
int vertex2;
float length;
};
bool compare(edge e1,edge e2){//sort
return e1.length<e2.length;
}
int main() {
int n;
while (cin >> n) {
//输入顶点集
vector<vertex>vertexarr(101);
vector<edge> edgearr;
for(int i=0;i<n;i++){
cin>>vertexarr[i].x>>vertexarr[i].y;
}
for(int i=0;i<n;i++){//构造边集
for(int j=i+1;j<n;j++){
edge e;
e.vertex1=i;
e.vertex2=j;
e.length=sqrt(pow(vertexarr[i].x-vertexarr[j].x,2)+pow(vertexarr[i].y-vertexarr[j].y,2));
edgearr.push_back(e);
}
}
sort(edgearr.begin(),edgearr.end(),compare);//升序
InitDisjointSet(n);
float TotalLength=0;
int curedgenum=0;
for(int i=0;i<edgearr.size();i++){//Kruskal
int u=edgearr[i].vertex1;
int v=edgearr[i].vertex2;
if(FindrootDisjointSet(u)!=FindrootDisjointSet(v)){
curedgenum++;
UnionDisjointSet(u,v);
TotalLength+=edgearr[i].length;
if(curedgenum==n-1){
break;
}
}
}
cout<<fixed;
cout.precision(2);
cout << TotalLength << endl;
}
}