Problem:
Problem G: Yu-kun Likes a Treasure
Background
会津大学付属幼稚園はプログラミングが大好きな子供が集まる幼稚園である。園児の一人であるゆう君は、プログラミングと同じくらい財宝が大好きだ。ゆう君は今日も財宝を探すため洞窟を探索する予定だ。そこでゆう君は洞窟の地図から、洞窟を全て見て回る際に歩く距離を求めるプログラムを書くことにした。
Problem
洞窟の情報とゆう君の初期位置が与えられる。Fig.1のように、 洞窟は2次元平面上の多角形で表される部屋が、１つ以上繋がった集合で構成される。部屋が別の部屋と接する場合は必ず点で接し、その接点が洞窟の道となり、接点に訪れるとその道を通ったことになる。
道は扉で閉ざされており、扉が閉まっている状態でその道を通ることはできない。扉を開くためには部屋の中にちょうど１つだけ存在するボタンを押す必要がある。ボタンは2次元平面上の点として表される。
ゆう君はボタンが存在する点を訪れることでボタンを押すことができる。ボタンを押すと部屋にある全ての扉が開く。Fig.1では部屋の中にある丸がボタンを表し、顔のマークがゆう君を、そして矢印が指しているボタンがゆう君のいる場所を表す。ボタンを押して扉を開いた後、道を通るとすぐに全ての扉は閉まる。そのため、そこから別の部屋へと移動するためには再度その部屋の中にあるボタンを押す必要がある。ゆう君は最初、いずれかの部屋の中にあるボタンと同じ場所にいる。
Fig. 1
Fig. 2 に移動の例を示す。番号の付いている矢印はゆう君の移動経路を表す。
次の順番で移動することは可能である。
1 -> 2 -> 3 -> 4
次の順番で移動することは不可能である。
1 -> 4
これは、1の経路で移動し部屋同士の接点を訪れるとその瞬間に扉が閉まるからである。
Fig. 2
ゆう君が洞窟内の全ての道を通り、再度初期位置に戻ってくるまでにかかる移動距離の最小値を出力せよ。ただし、同じ道を何度通っても良い。
Input
入力は以下の形式で与えられる。
n
m
部屋1の情報
部屋2の情報
…
部屋
n
の情報
n
,
m
はそれぞれ洞窟を構成する部屋を表す多角形の数、ゆう君が最初にいる部屋の番号を表す。
部屋の情報は次の形式で与えられる。
p
x
1
y
1
x
2
y
2
…
x
p
y
p
bx
by
p
は多角形の頂点数を表す。(
x
i
,
y
i
) と (
x
i+1
,
y
i+1
) の頂点を繋いだ線分が多角形の一辺である。ただし、
p
番目の頂点は１番目の頂点と繋がる。 (
bx
,
by
) は多角形の内部にあるボタンの位置を表す。多角形は反時計回りで与えられる。
Constraints
入力は以下の制約を満たす。
1 ≤
n
≤ 15
1 ≤
m
≤
n
3 ≤
p
≤ 20
0 ≤
x
,
y
,
bx
,
by
≤ 1000
多角形は反時計回りで与えられる
多角形が別の多角形を内包するようなことはない
多角形はその内部 ( 多角形の辺は含まない ) にちょうど１つだけボタンを持つ
任意の異なる２つの多角形は共通な面積を持たない
任意の異なる２つの多角形
a
,
b
において、
a
と
b
の接点は高々１つしかない
任意の異なる２つの多角形
a
,
b
において、直接または１つ以上の多角形を経由することで
a
から
b
へ移動することができる
Output
ゆう君が初期位置から全ての道を通って再度初期位置に戻ってくるまでにかかる移動の距離の総和の最小値を出力せよ。小数点以下は何桁数字を出力しても構わない。ただし、解答の誤差は0.00001(10
-5
)を超えてはならない。
Sample Input1
3 1
4
0 4
2 4
2 7
0 7
1 6
5
1 0
6 0
4 5
2 2
1 4
4 2
3
2 6
6 4
6 7
5 6
Sample Output1
14.6502815399
Sample Input2
3 2
3
0 2
3 5
0 5
1 4
3
1 0
4 0
1 3
2 1
3
2 2
4 3
2 4
3 3
Sample Output2
8.0644951022
Sample Input2
4 4
3
0 5
3 8
0 8
1 7
3
1 3
4 3
1 6
2 4
5
2 0
7 0
7 2
3 2
2 3
6 1
3
6 2
7 7
2 7
6 6
Sample Output2
18.9356648257

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define fi first
#define se second
#define dbg(x) cout<<#x" = "<<((x))<<endl
template<class T,class U> ostream& operator<<(ostream& o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}
template<class T> ostream& operator<<(ostream& o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}

/* 基本要素 */
typedef complex<double> Point;
typedef pair<Point, Point> Line;
typedef vector<Point> VP;
const double PI = acos(-1);
const double EPS = 1e-7; // 許容誤差^2
const double INF = 1e9;
#define X real()
#define Y imag()
#define LE(n,m) ((n) - (m) < EPS)
#define GE(n,m) (EPS > (m) - (n))
#define EQ(n,m) (abs((n)-(m)) < EPS)

// 内積　dot(a,b) = |a||b|cosθ
double dot(Point a, Point b) {
  return a.X*b.X + a.Y*b.Y;
}

// 外積　cross(a,b) = |a||b|sinθ
double cross(Point a, Point b) {
  return a.X*b.Y - a.Y*b.X;
}

// 点の進行方向
// !!! 誤差に注意 !!! (掛け算したものとかなり小さいものを比べているので)
int ccw(Point a, Point b, Point c) {
  b -= a;  c -= a;
  if (cross(b,c) >  EPS) return +1;  // counter clockwise
  if (cross(b,c) < -EPS) return -1;  // clockwise
  if (dot(b,c)   < -EPS) return +2;  // c--a--b on line
  if (norm(b) < norm(c)) return -2;  // a--b--c on line or a==b
  return 0;                          // a--c--b on line or a==c or b==c
}

// 線分と線分
bool isecSS(Point a1, Point a2, Point b1, Point b2) {
  return ccw(a1, a2, b1)*ccw(a1, a2, b2) <= 0 &&
  ccw(b1, b2, a1)*ccw(b1, b2, a2) <= 0;
}

// 線分と点
bool isecSP(Point a1, Point a2, Point b) {
  return !ccw(a1, a2, b);
}

// 2直線の交点
Point crosspointLL(Point a1, Point a2, Point b1, Point b2) {
  double d1 = cross(b2-b1, b1-a1);
  double d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  assert(!EQ(d2, 0)); // 交点がない
  return a1 + d1/d2 * (a2-a1);
}

// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(Point p, const VP& ps) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    Point a = ps[i] - p;
    Point b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}

Point READ(){
    int x,y;
    cin >>x >>y;
    return Point(x,y);
}

int main(){
    int n,m;
    cin >>n >>m;

    vector<VP> room(n);
    vector<int> sz(n);
    VP b(n);
    rep(i,n){
        cin >>sz[i];
        rep(j,sz[i]) room[i].pb(READ());
        b[i] = READ();
    }

    vector<int> deg(n);
    vector<vector<bool>> touch(n,(vector<bool>(n)));
    vector<VP> cp(n, VP(n));

    // iとjが接する場所
    rep(i,n)rep(j,i){
        bool tmp = false;
        Point pp;
        for(Point p:room[i]){
            rep(k,sz[j])if(isecSP(room[j][k], room[j][(k+1)%sz[j]], p)){
                tmp = true;
                pp = p;
            }
        }
        for(Point p:room[j]){
            rep(k,sz[i])if(isecSP(room[i][k], room[i][(k+1)%sz[i]], p)){
                tmp = true;
                pp = p;
            }
        }

        if(tmp){
            touch[i][j] = touch[j][i] = true;
            ++deg[i];
            ++deg[j];
            cp[i][j] = cp[j][i] = pp;
        }
    }

    vector<vector<double>> d(n, vector<double>(n,INF));
    rep(i,n){
        VP ps;
        ps.pb(b[i]);
        rep(j,sz[i]) ps.pb(room[i][j]);

        int V = sz[i]+1;
        vector<int> idx(n,-1);
        rep(j,n)if(touch[i][j]){
            ps.pb(cp[i][j]);
            idx[j] = V++;
        }

        vector<double> dist(V,INF);
        dist[0] = 0;
        queue<int> que({0});
        while(!que.empty()){
            int a = que.front();
            que.pop();
            rep(j,V)if(j!=a){
                bool can_move = true;
                rep(k,sz[i]){
                    Point p1 = room[i][k], p2 = room[i][(k+1)%sz[i]];
                    if(isecSS(ps[a],ps[j],p1,p2)){
                        Point cand = crosspointLL(ps[a],ps[j],p1,p2);
                        bool edge = false;
                        for(Point ep:{ps[a],ps[j]}){
                            if(abs(ep-cand) < EPS) edge = true;
                        }

                        if(!edge){
                            can_move = false;
                            break;
                        }
                    }
                }
                if(inPolygon( (ps[a]+ps[j])*0.5, room[i] ) == 0) can_move = false;

                if(can_move){
                    double add = abs(ps[a]-ps[j]);
                    if( dist[a]+add+EPS < dist[j] ){
                        dist[j] = dist[a]+add;
                        que.push(j);
                    }
                }
            }
        }

        rep(j,n)if(touch[i][j]) d[i][j] = dist[idx[j]];
    }

    int start = 0;
    rep(i,n)if(deg[i]%2 == 1) start |= (1<<i);
    double s_sum = 0;
    rep(i,n)rep(j,n)if(touch[i][j]) s_sum += d[i][j];

    vector<double> dp(1<<n,INF);
    dp[start] = s_sum;

    using P = pair<double, int>;
    priority_queue<P, vector<P>, greater<P>> pq;
    pq.push({s_sum, start});
    while(!pq.empty()){
        P now = pq.top();
        pq.pop();
        int mask = now.se;
        if(now.fi > dp[mask]) continue;

        rep(i,n)rep(j,i)if(touch[i][j]){
            int nx = mask^(1<<i)^(1<<j);
            double add = d[i][j] + d[j][i];
            if(dp[mask]+add+EPS < dp[nx]){
                dp[nx] = dp[mask]+add;
                pq.push({dp[nx], nx});
            }
        }
    }

    printf("%.10f\n", dp[0]);
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3796
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define fi first
#define se second
#define dbg(x) cout<<#x" = "<<((x))<<endl
template<class T,class U> ostream& operator<<(ostream& o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}
template<class T> ostream& operator<<(ostream& o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}

/* 基本要素 */
typedef complex<double> Point;
typedef pair<Point, Point> Line;
typedef vector<Point> VP;
const double PI = acos(-1);
const double EPS = 1e-7; // 許容誤差^2
const double INF = 1e9;
#define X real()
#define Y imag()
#define LE(n,m) ((n) - (m) < EPS)
#define GE(n,m) (EPS > (m) - (n))
#define EQ(n,m) (abs((n)-(m)) < EPS)

// 内積　dot(a,b) = |a||b|cosθ
double dot(Point a, Point b) {
  return a.X*b.X + a.Y*b.Y;
}

// 外積　cross(a,b) = |a||b|sinθ
double cross(Point a, Point b) {
  return a.X*b.Y - a.Y*b.X;
}

// 点の進行方向
// !!! 誤差に注意 !!! (掛け算したものとかなり小さいものを比べているので)
int ccw(Point a, Point b, Point c) {
  b -= a;  c -= a;
  if (cross(b,c) >  EPS) return +1;  // counter clockwise
  if (cross(b,c) < -EPS) return -1;  // clockwise
  if (dot(b,c)   < -EPS) return +2;  // c--a--b on line
  if (norm(b) < norm(c)) return -2;  // a--b--c on line or a==b
  return 0;                          // a--c--b on line or a==c or b==c
}

// 線分と線分
bool isecSS(Point a1, Point a2, Point b1, Point b2) {
  return ccw(a1, a2, b1)*ccw(a1, a2, b2) <= 0 &&
  ccw(b1, b2, a1)*ccw(b1, b2, a2) <= 0;
}


// 線分と点
bool isecSP(Point a1, Point a2, Point b) {
  return !ccw(a1, a2, b);
}

// 2直線の交点
Point crosspointLL(Point a1, Point a2, Point b1, Point b2) {
  double d1 = cross(b2-b1, b1-a1);
  double d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  assert(!EQ(d2, 0)); // 交点がない
  return a1 + d1/d2 * (a2-a1);
}

// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(Point p, const VP& ps) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    Point a = ps[i] - p;
    Point b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}

Point READ(){
    int x,y;
    cin >>x >>y;
    return Point(x,y);
}

int main(){
    int n,m;
    cin >>n >>m;

    vector<VP> room(n);
    vector<int> sz(n);
    VP b(n);
    rep(i,n){
        cin >>sz[i];
        rep(j,sz[i]) room[i].pb(READ());
        b[i] = READ();
    }

    vector<int> deg(n);
    vector<vector<bool>> touch(n,(vector<bool>(n)));
    vector<VP> cp(n, VP(n));

    // iとjが接する場所
    rep(i,n)rep(j,i){

        bool tmp = false;
        Point pp;
        for(Point p:room[i]){
            rep(k,sz[j]){
                if(isecSP(room[j][k], room[j][(k+1)%sz[j]], p)){
                    tmp = true;
                    pp = p;
                }
            }
        }
        for(Point p:room[j]){
            rep(k,sz[i]){
                if(isecSP(room[i][k], room[i][(k+1)%sz[i]], p)){
                    tmp = true;
                    pp = p;
                }
            }
        }

        if(tmp){
            // printf(" TOUCH ! %d %d  ",i,j); dbg(pp);

            touch[i][j] = touch[j][i] = true;
            ++deg[i];
            ++deg[j];
            cp[i][j] = cp[j][i] = pp;
        }
    }

    vector<vector<double>> d(n, vector<double>(n,INF));
    rep(i,n){

        VP ps;
        ps.pb(b[i]);
        rep(j,sz[i]) ps.pb(room[i][j]);

        int now = sz[i]+1;
        vector<int> idx(n,-1);
        rep(j,n)if(touch[i][j]){
            ps.pb(cp[i][j]);
            idx[j] = now++;
        }

        int V = ps.size();
        vector<double> dist(V,INF);
        dist[0] = 0;
        queue<int> que({0});
        while(!que.empty()){
            int a = que.front();
            que.pop();
            rep(j,V)if(j!=a){
                bool can_move = true;
                rep(k,sz[i]){
                    Point p1 = room[i][k], p2 = room[i][(k+1)%sz[i]];
                    if(isecSS(ps[a],ps[j],p1,p2)){
                        Point cand = crosspointLL(ps[a],ps[j],p1,p2);
                        bool edge = false;
                        for(Point ep:{ps[a],ps[j]}){
                            if(abs(ep-cand) < EPS) edge = true;
                        }

                        if(!edge){
                            can_move = false;
                            break;
                        }
                    }
                }

                if(inPolygon( (ps[a]+ps[j])*0.5, room[i] ) == 0) can_move = false;

                if(can_move){
                    double add = abs(ps[a]-ps[j]);
                    if( dist[a]+add+EPS < dist[j] ){
                        // printf(" MOVE %d -> %d  :: %f\n",a,j,add);
                        dist[j] = dist[a]+add;
                        que.push(j);
                    }
                }
            }
        }

        // dbg(dist);
        rep(j,n)if(touch[i][j]) d[i][j] = dist[idx[j]];
    }

    // rep(i,n){
    //     rep(j,n)if(touch[i][j]){
    //         printf(" %d  -> %d   = %f\n",i,j,d[i][j]);
    //     }
    // }

    int start = 0;
    rep(i,n)if(deg[i]%2 == 1) start |= (1<<i);

    double s_sum = 0;
    rep(i,n)rep(j,n)if(touch[i][j]) s_sum += d[i][j];

    vector<double> dp(1<<n,INF);
    dp[start] = s_sum;

    using P = pair<double, int>;
    priority_queue<P, vector<P>, greater<P>> pq;
    pq.push({s_sum, start});
    while(!pq.empty()){
        P now = pq.top();
        pq.pop();
        int mask = now.se;
        if(now.fi > dp[mask]) continue;

        rep(i,n)rep(j,i)if(touch[i][j]){
            int nx = mask;
            nx ^= (1<<i);
            nx ^= (1<<j);

            double add = d[i][j] + d[j][i];
            if(dp[mask]+add+EPS < dp[nx]){
                dp[nx] = dp[mask]+add;
                pq.push({dp[nx], nx});
            }
        }
    }

    printf("%.10f\n", dp[0]);
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3756
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define fi first
#define se second
#define dbg(x) cout<<#x" = "<<((x))<<endl
template<class T,class U> ostream& operator<<(ostream& o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}
template<class T> ostream& operator<<(ostream& o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}

/* 基本要素 */
typedef complex<double> Point;
typedef pair<Point, Point> Line;
typedef vector<Point> VP;
const double PI = acos(-1);
const double EPS = 1e-7; // 許容誤差^2
const double INF = 1e9;
#define X real()
#define Y imag()
#define LE(n,m) ((n) - (m) < EPS)
#define GE(n,m) (EPS > (m) - (n))
#define EQ(n,m) (abs((n)-(m)) < EPS)

// 内積　dot(a,b) = |a||b|cosθ
double dot(Point a, Point b) {
  return a.X*b.X + a.Y*b.Y;
}

// 外積　cross(a,b) = |a||b|sinθ
double cross(Point a, Point b) {
  return a.X*b.Y - a.Y*b.X;
}

// 点の進行方向
// !!! 誤差に注意 !!! (掛け算したものとかなり小さいものを比べているので)
int ccw(Point a, Point b, Point c) {
  b -= a;  c -= a;
  if (cross(b,c) >  EPS) return +1;  // counter clockwise
  if (cross(b,c) < -EPS) return -1;  // clockwise
  if (dot(b,c)   < -EPS) return +2;  // c--a--b on line
  if (norm(b) < norm(c)) return -2;  // a--b--c on line or a==b
  return 0;                          // a--c--b on line or a==c or b==c
}

// 線分と線分
bool isecSS(Point a1, Point a2, Point b1, Point b2) {
  return ccw(a1, a2, b1)*ccw(a1, a2, b2) <= 0 &&
  ccw(b1, b2, a1)*ccw(b1, b2, a2) <= 0;
}


// 線分と点
bool isecSP(Point a1, Point a2, Point b) {
  return !ccw(a1, a2, b);
}

// 2直線の交点
Point crosspointLL(Point a1, Point a2, Point b1, Point b2) {
  double d1 = cross(b2-b1, b1-a1);
  double d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  assert(!EQ(d2, 0)); // 交点がない
  return a1 + d1/d2 * (a2-a1);
}

// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(Point p, const VP& ps) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    Point a = ps[i] - p;
    Point b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}

Point READ(){
    int x,y;
    cin >>x >>y;
    return Point(x,y);
}

int main(){
    int n,m;
    cin >>n >>m;

    vector<VP> room(n);
    vector<int> sz(n);
    VP b(n);
    rep(i,n){
        cin >>sz[i];
        rep(j,sz[i]) room[i].pb(READ());
        b[i] = READ();
    }

    vector<int> deg(n);
    vector<vector<bool>> touch(n,(vector<bool>(n)));
    vector<VP> cp(n, VP(n));

    // iとjが接する場所
    rep(i,n)rep(j,i){

        bool tmp = false;
        Point pp;
        for(Point p:room[i]){
            rep(k,sz[j]){
                if(isecSP(room[j][k], room[j][(k+1)%sz[j]], p)){
                    tmp = true;
                    pp = p;
                }
            }
        }
        for(Point p:room[j]){
            rep(k,sz[i]){
                if(isecSP(room[i][k], room[i][(k+1)%sz[i]], p)){
                    tmp = true;
                    pp = p;
                }
            }
        }

        if(tmp){
            // printf(" TOUCH ! %d %d  ",i,j); dbg(pp);

            touch[i][j] = touch[j][i] = true;
            ++deg[i];
            ++deg[j];
            cp[i][j] = cp[j][i] = pp;
        }
    }

    vector<vector<double>> d(n, vector<double>(n,INF));
    rep(i,n){

        VP ps;
        ps.pb(b[i]);
        rep(j,sz[i]) ps.pb(room[i][j]);

        int now = sz[i]+1;
        vector<int> idx(n,-1);
        rep(j,n)if(touch[i][j]){
            ps.pb(cp[i][j]);
            idx[j] = now++;
        }

        int V = ps.size();
        vector<double> dist(V,INF);
        dist[0] = 0;
        queue<int> que({0});
        while(!que.empty()){
            int a = que.front();
            que.pop();
            rep(j,V)if(j!=a){
                bool can_move = true;
                rep(k,sz[i]){
                    Point p1 = room[i][k], p2 = room[i][(k+1)%sz[i]];
                    if(isecSS(ps[a],ps[j],p1,p2)){
                        Point cand = crosspointLL(ps[a],ps[j],p1,p2);
                        bool edge = false;
                        for(Point ep:{ps[a],ps[j]}){
                            if(abs(ep-cand) < EPS) edge = true;
                        }

                        if(!edge){
                            can_move = false;
                            break;
                        }
                    }
                }

                if(inPolygon( (ps[a]+ps[j])*0.5, room[i] ) == 0) can_move = false;

                if(can_move){
                    double add = abs(ps[a]-ps[j]);
                    if( dist[a]+add+EPS < dist[j] ){
                        // printf(" MOVE %d -> %d  :: %f\n",a,j,add);
                        dist[j] = dist[a]+add;
                        que.push(j);
                    }
                }
            }
        }

        // dbg(dist);
        rep(j,n)if(touch[i][j]) d[i][j] = dist[idx[j]];
    }

    // rep(i,n){
    //     rep(j,n)if(touch[i][j]){
    //         printf(" %d  -> %d   = %f\n",i,j,d[i][j]);
    //     }
    // }

    int start = 0;
    rep(i,n)if(deg[i]%2 == 1) start |= (1<<i);

    double s_sum = 0;
    rep(i,n)rep(j,n)if(touch[i][j]) s_sum += d[i][j];

    vector<double> dp(1<<n,INF);
    dp[start] = s_sum;

    using P = pair<double, int>;
    priority_queue<P, vector<P>, greater<P>> pq;
    pq.push({s_sum, start});
    while(!pq.empty()){
        P now = pq.top();
        pq.pop();
        int mask = now.se;
        if(now.fi > dp[mask]) continue;

        rep(i,n)rep(j,n)if(touch[i][j]){
            int nx = mask;
            nx ^= (1<<i);
            nx ^= (1<<j);

            double add = d[i][j] + d[j][i];
            if(dp[mask]+add+EPS < dp[nx]){
                dp[nx] = dp[mask]+add;
                pq.push({dp[nx], nx});
            }
        }
    }

    printf("%.10f\n", dp[0]);
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3796
final_rank: 3