#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=1100; const int INF=1<<29; const double EPS=0.0000001; double dp[maxn][maxn]; int n; struct Point { int x,y; };Point p[maxn]; double dist(Point A,Point B) { double tx=A.x-B.x,ty=A.y-B.y; return sqrt(tx*tx+ty*ty); } int main() { freopen("in.txt","r",stdin); while(cin>>n){ REP(i,1,n) scanf("%d%d",&p[i].x,&p[i].y); REP(i,1,n) REP(j,1,n) dp[i][j]=INF*1.0; dp[1][1]=0; REP(i,1,n){ REP(j,1,n){ if(i>=j){ if(i+1!=j||i+1==n) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+dist(p[i],p[i+1])); if(i!=n) dp[i][i+1]=min(dp[i][i+1],dp[i][j]+dist(p[j],p[i+1])); else dp[i][i]=min(dp[i][i],dp[i][j]+dist(p[j],p[i])); } else{ if(j+1!=i||j+1==n) dp[i][j+1]=min(dp[i][j+1],dp[i][j]+dist(p[j],p[j+1])); if(j!=n) dp[j+1][j]=min(dp[j+1][j],dp[i][j]+dist(p[i],p[j+1])); else dp[j][j]=min(dp[j][j],dp[i][j]+dist(p[i],p[j])); } } } /* REP(i,1,n){ REP(j,1,n){ printf("%15.2f ",dp[i][j]); } puts(""); } */ printf("%.2f\n",dp[n][n]); } return 0; } /** dp决策的考虑的两种方式: 用一般方法考虑状态转移:从哪里转移过来。 用刷表法考虑状态转移:转移到哪里去。 */