//考点最小生成树
//用到并查集和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;
    }
}