Problem:
Chain-Confined Path
There is a chain consisting of multiple circles on a plane.
The first (last) circle of the chain only intersects with the next (previous) circle,
and each intermediate circle intersects only with the two neighboring circles.
Your task is to find the shortest path that satisfies the following conditions.
The path connects the centers of the first circle and the last circle.
The path is confined in the chain, that is, all the points on the path are located within or on at least one of the circles.
Figure E-1 shows an example of such a chain and the corresponding shortest path.
Figure E-1: An  example chain and the corresponding shortest path
Input
The input consists of multiple datasets.
Each dataset represents the shape of a chain in the following format.
n
x
1
y
1
r
1
x
2
y
2
r
2
...
x
n
y
n
r
n
The first line of a dataset contains an integer
n
(3 ≤
n
≤ 100) 
representing the number of the circles.
Each of the following
n
lines contains three integers separated by a single space.
(
x
i
,
y
i
) 
and
r
i
represent 
the center position and the radius of the
i
-th circle
C
i
.
You can assume that 0 ≤
x
i
≤ 1000, 
0 ≤
y
i
≤ 1000, and
1 ≤
r
i
≤ 25.
You can assume that
C
i
and
C
i
+1
(1 ≤
i
≤
n
−1) intersect at two separate points.
When
j
≥
i
+2,
C
i
and
C
j
are apart and either of them does not contain the other.
In addition, you can assume that any circle does not contain the center of any other circle.
The end of the input is indicated by a line containing a zero.
Figure E-1 corresponds to the first dataset of Sample Input below.
Figure E-2 shows the shortest paths for the subsequent datasets of Sample Input.
Figure E-2: Example chains and the corresponding shortest paths
Output
For each dataset, output a single line containing the length of the shortest chain-confined path between the centers of the first circle and the last circle.
The value should not have an error greater than 0.001.
No extra characters should appear in the output.
Sample Input
10
802 0 10
814 0 4
820 1 4
826 1 4
832 3 5
838 5 5
845 7 3
849 10 3
853 14 4
857 18 3
3
0 0 5
8 0 5
8 8 5
3
0 0 5
7 3 6
16 0 5
9
0 3 5
8 0 8
19 2 8
23 14 6
23 21 6
23 28 6
19 40 8
8 42 8
0 39 5
11
0 0 5
8 0 5
18 8 10
8 16 5
0 16 5
0 24 5
3 32 5
10 32 5
17 28 8
27 25 3
30 18 5
0
Output for the Sample Input
58.953437
11.414214
16.0
61.874812
63.195179

Submitted solutions:

code_snippet: /*
 * Author:  xioumu
 * Created Time:  2012/7/20 10:46:25
 * File Name: e.cpp
 * solve: e.cpp
 */
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<map>
#include<set>
using namespace std;
#define sz(v) ((int)(v).size())
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define repf(i, a, b) for (int i = (a); i <= (b); ++i)
#define repd(i, a, b) for (int i = (a); i >= (b); --i)
typedef long long lint;
const int maxint = -1u>>1;
const double esp = 1e-8;
const int maxn = 107;
int sgn(double x){
    return (x > esp) - (x < -esp);
}
double sqr(double x){
    return x * x;
}
struct point{
    double x, y;
    point(double _x = 0, double _y = 0) : x(_x), y(_y){}
    point operator + (const point& b) const{
        return point(x + b.x, y + b.y);
    }
    point operator - (const point& b) const{
        return point(x - b.x, y - b.y);
    }
    point operator * (const double &p) const{
        return point(x * p, y * p);
    }
    point operator / (const double &p) const{
        return point (x / p, y / p);
    }
    double operator * (const point& b) const{
        return x * b.y - b.x * y;
    }
    double len(){
        return sqrt(x * x + y * y);
    }
    point set(){
        double l = sqrt(x * x + y * y);
        return point(x / l, y / l); 
    }
    void output(){
        printf("%f %f\n", x, y);
    }
};
int n;
double r[maxn];
point cen[maxn], int_cir[maxn][3];
//void cha(point a, point b, 
void inter_cir(int u, int v, point &p1, point &p2){
    point d0 = (cen[u] - cen[v]);
    point d1, d2;
    d1 = d0;
    d1 = point(d1.y, -d1.x);
    d2 = d1 * -1;
    double x1, x2, h;
    double a, b, c;
    a = r[v];
    b = r[u];
    c = (cen[v] - cen[u]).len();
    double coss = (c * c + a * a - b * b) / (2 * a * c);
    double sins = sqrt(1 - coss * coss);
    h = a * sins;
    x2 = a * coss; 
    //x2 = sqrt(r[v] * r[v] - h * h);
    p1 = cen[v] + d0 * x2 / d0.len() + d1.set() * h;
    p2 = cen[v] + d0 * x2 / d0.len() + d2 * h / d2.len() ;
    //printf("%f\n", x2);
    //printf("(%f, %f)\n", (p1 - cen[v]).len(), (p1 - cen[u]).len() );
    //printf("(%f, %f)\n", (p2 - cen[v]).len(), (p2 - cen[u]).len() );

}

void getpoint(){
    point p1, p2; 
    repf (i, 1, n - 1){
        inter_cir(i - 1, i, p1, p2); 
        int_cir[i][0] = p1;
        int_cir[i][1] = p2; 
    }
    int_cir[0][0] = int_cir[0][1] = cen[0];
    int_cir[n][0] = int_cir[n][1] = cen[n - 1];

    //for(int i = 1; i <= n; i++)
        //for(int j = 0; j < 2; j++)
            //int_cir[i][j].output();
}

double get_h(point o, point a, point b){
    //a = a + (b - a).set() * 100;
    //b = b + (a - b).set() * 100;
    return fabs((a - o) * (b - o)) / (a - b).len();
}

bool across(int w, point a, point b){
    double h = get_h(cen[w], a, b);
    if(sgn(r[w] - h) <= 0) return false;
    else return true;
}

void get_int(int w, point a, point b, point& p1, point& p2){
    double h = get_h(cen[w], a, b);
    double x1 = sqrt( sqr( (a - cen[w]).len() ) - sqr(h) );
    double x2 = sqrt( sqr(r[w]) - sqr(h) );
    p1 = a + (b - a).set() * (x1 - x2);
    p2 = a + (b - a).set() * (x1 + x2);
    //printf("(%f,%f)\n", (p1 - cen[w]).len(), (p2 - cen[w]).len());
}

bool in(point a, int w){
    return sgn( r[w] - (a - cen[w]).len() ) > 0;
}

bool connect(int w, int r, point a, point b){
    //if(r == 0 && w == 2){
        //printf("%d %d %.8f\n", r, w, (a - b).len() );
        //a.output();
        //b.output();
        //cen[1].output();
        //printf("## %.8f ##\n\n", get_h(cen[1], a, b));
    //}
    for(int i = r; i < w; i++)
        if( !across(i, a, b) ){
            //if(i == 1 && r == 1 && w == 2){
            //printf("---\n");
            //printf("%d %d %f\n", r, w, (a - b).len());
            //a.output();
            //b.output();
            //cen[i].output();
            //printf("%f\n%f\n", fabs( (a - cen[i]) * (b - cen[i])) , (a -cen[i]).len() );
            //printf("%.8f\n", fabs( (a - cen[i]) * (b - cen[i])) * 1.0 / (a -b).len());
            //printf("## %f ##\n\n", get_h(cen[i], a, b));
            //}
            return false;
        }
    for(int i = r; i < w - 1; i++){
        point p[5];
        get_int(i, a, b, p[1], p[2]);
        get_int(i + 1, a, b, p[3], p[4]);
        for(int j = 1; j <= 2; j++)
            for(int k = 3; k <= 4; k++)
                if( !in( ( p[j] + p[k]) / 2, i) && !in( (p[j] + p[k]) / 2, i + 1) )
                    return false;
    }
    return true;
}
double dis[maxn][3][maxn][3];
void getline(){
    repf(i, 0, n + 1)
        repf(ki, 0, 1)
            repf(j, 0, n + 1)
                repf(kj, 0, 1)
                    dis[i][ki][j][kj] = 1e100;
    for(int i = 1; i <= n; i++)
        for(int ki = 0; ki <= 1; ki++)
            for(int j = 0; j < i; j++){
                for(int kj = 0; kj <= 1; kj++){
                    //printf("+===%f=====\n",dis[j][kj][i][ki]);
                    if( connect(i, j, int_cir[i][ki], int_cir[j][kj]) ) {
                        dis[i][ki][j][kj] = (int_cir[i][ki] - int_cir[j][kj]).len();
                        dis[j][kj][i][ki] = dis[i][ki][j][kj];
                        //if(i == 2 && j == 1) printf("+===%f=====\n",dis[j][kj][i][ki]);
                    }
                }
            }
}
void init(){
    rep (i, n){
        double x, y;
        scanf("%lf%lf%lf\n", &x, &y, &r[i]);    
        cen[i] = point(x, y);
    }
}
double dp[maxn][3];
void getdp(){
    repf(i, 0, n)
        repf(j, 0, 1)
            dp[i][j] = 1e100;
    dp[0][0] = 0;
    for(int i = 1; i <= n; i++)
        for(int ki = 0; ki <= 1; ki++)
            for(int j = 0; j < i; j++)
                for(int kj = 0; kj <= 1; kj++)
                    if(dp[i][ki] > dp[j][kj] + dis[i][ki][j][kj]){
                        dp[i][ki] = dp[j][kj] + dis[i][ki][j][kj];
                        //if(i == 2 && j == 1)
                            //printf(" %d %d+===%f=====\n", j, i, dis[j][kj][i][ki]);
                    }
    //printf("========================\n");
    //for(int i = 0; i <= n; i++, printf("\n"))
        //for(int ki = 0; ki <= 1; ki++)
            //printf("%f ", dp[i][ki]);
    printf("%f\n",dp[n][0]);
}
int main(){
    while(scanf("%d", &n) && n){
        init();
        getpoint();
        getline();
        getdp();
    }
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3792
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ld = long double;
#undef long
#define long long long
#define ll long
#define vec vector
#define rep(i, n) for (long i = 0; i < n; i++)
#define all(a) begin(a), end(a)
#define sz(a) (int)(a).size()
template <typename T>
bool chmin(T &x, T y)
{
    if (y < x)
    {
        x = y;
        return true;
    }
    return false;
}
template <typename T>
bool chmax(T &x, T y)
{
    if (x < y)
    {
        x = y;
        return true;
    }
    return false;
}
template <typename T>
ostream &operator<<(ostream &os, vector<T> a)
{
    const int n = a.size();
    rep(i, n)
    {
        os << a[i];
        if (i + 1 != n)
            os << " ";
    }
    return os;
}
using R = ld;
using point = complex<R>;
const R EPS(1e-10), PI(acosl(-1)), INF(1e18);
inline bool eq(const R &a, const R &b) { return fabsl(b - a) < EPS; }
inline int compare(const R &a, const R &b) { return eq(a, b) ? 0 : a < b ? -1
                                                                         : 1; }
struct circle
{
    point center;
    R radius;
    circle() = default;
    circle(const point &center, const R &radius) : center(center), radius(radius) {}
    friend std::ostream &operator<<(std::ostream &os, const circle &c)
    {
        return os << '(' << c.center << ", " << c.radius << ')';
    }
    friend std::istream &operator>>(std::istream &is, circle &c) { return is >> c.center >> c.radius; }
};
using points = vec<point>;
points crosspoints(const circle &c1, const circle &c2)
{
    R d = abs(c1.center - c2.center);
    if (compare(d, c1.radius + c2.radius) == 1)
        return points(0);
    if (compare(d, fabsl(c1.radius - c2.radius)) == -1)
        return points(0);
    bool one_crosspoint = false;
    if (eq(d, c1.radius + c2.radius) || eq(d, fabsl(c1.radius - c2.radius)))
        one_crosspoint = true;
    const R alpha =
        acosl((c1.radius * c1.radius + d * d - c2.radius * c2.radius) / (2 * c1.radius * d)); // cosine theorem
    const R beta = std::arg(c2.center - c1.center);
    if (one_crosspoint)
        return points{c1.center + std::polar(c1.radius, beta + alpha)};
    return points{c1.center + std::polar(c1.radius, beta + alpha), c1.center + std::polar(c1.radius, beta - alpha)};
}
struct segment;
struct line
{
    point a, b;
    line() = default;
    line(const point &a, const point &b) : a(a), b(b) {}
    // Ax + By + C = 0
    line(const R &A, const R &B, const R &C)
    {
        if (eq(A, 0))
        {
            assert(!eq(B, 0));
            a = point(0, -C / B), b = point(1, -(A + C) / B);
        }
        else
        {
            a = point(-C / A, 0), b = point(-(B + C) / A, 1);
        }
    }
    explicit line(const segment &seg);
    friend std::ostream &operator<<(std::ostream &os, const line &ln)
    {
        return os << '(' << ln.a << " -- " << ln.b << ')';
    }
    friend std::istream &operator>>(std::istream &is, line &a) { return is >> a.a >> a.b; }
};
struct segment
{
    point a, b;
    segment() = default;
    segment(const point &a, const point &b) : a(a), b(b) {}
    explicit segment(const line &ln) : a(ln.a), b(ln.b) {}
    friend std::ostream &operator<<(std::ostream &os, const segment &seg)
    {
        return os << '[' << seg.a << " -- " << seg.b << ']';
    }
    friend std::istream &operator>>(std::istream &is, segment &a) { return is >> a.a >> a.b; }
};
line::line(const segment &seg) : a(seg.a), b(seg.b) {}
R dot(const point &a, const point &b) { return real(a) * real(b) + imag(a) * imag(b); }
using arrow = point;

inline int sgn(const R &x) { return fabsl(x) < EPS ? 0 : (x < 0 ? -1 : 1); }
point projection(const line &l, const point &p)
{
    return l.a + (l.a - l.b) * dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
}
points crosspoints(const circle &c, const line &l)
{
    const point pr = projection(l, c.center);
    const R square = c.radius * c.radius - norm(pr - c.center);
    switch (sgn(square))
    {
    case 0:
        return points{pr};
    case -1:
        return points(0);
    }
    const arrow v = (l.b - l.a) / abs(l.b - l.a) * sqrt(square);
    return points{pr - v, pr + v};
}
enum CCW
{
    ONLINE_FRONT = -2,     // a,b,c が一直線上
    CLOCKWISE = -1,        // c が a,b から時計回り側
    ON_SEGMENT = 0,        // a,c,b が一直線上
    COUNTER_CLOCKWISE = 1, // c が a,b から反時計回り側
    ONLINE_BACK = 2,       // c,a,b が一直線上
};
R cross(const point &a, const point &b) { return real(a) * imag(b) - imag(a) * real(b); }
int ccw(const point &a, point b, point c)
{
    b -= a, c -= a;
    const R crs_b_c = cross(b, c);
    if (crs_b_c > EPS)
        return CCW::COUNTER_CLOCKWISE;
    if (crs_b_c < -EPS)
        return CCW::CLOCKWISE;
    if (dot(b, c) < -EPS)
        return CCW::ONLINE_BACK;
    if (norm(b) + EPS < norm(c))
        return CCW::ONLINE_FRONT;
    return CCW::ON_SEGMENT;
}
int intersect(const segment &s, const point &p) { return int(ccw(s.a, s.b, p) == CCW::ON_SEGMENT); }
points crosspoints(const circle &c, const segment &s)
{
    points ret;
    for (const auto &pt : crosspoints(c, line(s)))
        if (intersect(s, pt))
            ret.push_back(pt);
    return ret;
}
void solve()
{
    while (true)
    {
        int n;
        cin >> n;
        if (n == 0)
            return;
        vec<circle> a(n);
        rep(i, n)
        {
            R x, y, r;
            cin >> x >> y >> r;
            a[i] = {{x, y}, r};
        }
        vec<points> b(n - 1);
        rep(i, n - 1)
        {
            b[i] = crosspoints(a[i], a[i + 1]);
            assert(sz(b[i]) == 2);
        }
        vec<vec<R>> d(n - 1, vec<R>(2, INF));
        R ans = INF;
        rep(i, n - 1)
        {
            rep(c0, 2)
            {
                { // 最初の円から
                    segment seg = {a[0].center, b[i][c0]};
                    bool ok = true;
                    vec<pair<R, bool>> res;
                    res.emplace_back(0, true);
                    res.emplace_back(a[0].radius, false);
                    res.emplace_back(abs(seg.a - seg.b), true);
                    for (int k = 1; k <= i; k++)
                    {
                        auto c = crosspoints(a[k], seg);
                        if (sz(c) != 2)
                        {
                            ok = false;
                            break;
                        }
                        R d1 = abs(c[0] - a[0].center), d2 = abs(c[1] - a[0].center);
                        if (d1 > d2)
                            swap(d1, d2);
                        res.emplace_back(d1, true);
                        res.emplace_back(d2, false);
                    }
                    sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                         {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
                    int cnt = 0;
                    for (auto [_, v] : res)
                    {
                        if (v)
                            cnt++;
                        else
                            cnt--;
                        ok &= cnt > 0;
                    }
                    if (ok)
                        chmin(d[i][c0], abs(a[0].center - b[i][c0]));
                }
                rep(j, i) rep(c1, 2)
                {
                    segment seg = {b[i][c0], b[j][c1]};
                    bool ok = true;
                    vec<pair<R, bool>> res;
                    res.emplace_back(abs(seg.a - seg.b), true);
                    for (int k = j + 1; k <= i; k++)
                    {
                        auto c = crosspoints(a[k], seg);
                        if (sz(c) != 2)
                        {
                            ok = false;
                            break;
                        }
                        R d1 = abs(c[0] - seg.a), d2 = abs(c[1] - seg.a);
                        if (d1 > d2)
                            swap(d1, d2);
                        res.emplace_back(d1, true);
                        res.emplace_back(d2, false);
                    }
                    sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                         {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
                    int cnt = 0;
                    for (auto [_, v] : res)
                    {
                        if (v)
                            cnt++;
                        else
                            cnt--;
                        ok &= cnt > 0;
                    }
                    if (ok)
                        chmin(d[i][c0], d[j][c1] + abs(seg.a - seg.b));
                }
            }
            R dm = abs(b[i][0] - b[i][1]);
            rep(c0, 2) chmin(d[i][c0], d[i][1 - c0] + dm);
            rep(c0, 2) // 最後の円へ
            {
                segment seg = {b[i][c0], a[n - 1].center};
                bool ok = true;
                vec<pair<R, bool>> res;
                res.emplace_back(abs(seg.a - seg.b) - a[n - 1].radius, true);
                for (int k = i + 1; k < n - 1; k++)
                {
                    auto c = crosspoints(a[k], seg);
                    if (sz(c) != 2)
                    {
                        ok = false;
                        break;
                    }
                    R d1 = abs(c[0] - seg.a), d2 = abs(c[1] - seg.a);
                    if (d1 > d2)
                        swap(d1, d2);
                    res.emplace_back(d1, true);
                    res.emplace_back(d2, false);
                }
                sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                     {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
                int cnt = 0;
                for (auto [_, v] : res)
                {
                    if (v)
                        cnt++;
                    else
                        cnt--;
                    ok &= cnt > 0;
                }
                if (ok)
                    chmin(ans, d[i][c0] + abs(seg.a - seg.b));
            }
        }
        {
            segment seg = {a[0].center, a[n - 1].center};
            bool ok = true;
            vec<pair<R, bool>> res;
            res.emplace_back(0, true);
            res.emplace_back(a[0].radius, false);
            res.emplace_back(abs(seg.a - seg.b) - a[n - 1].radius, true);
            for (int k = 1; k < n - 1; k++)
            {
                auto c = crosspoints(a[k], seg);
                if (sz(c) != 2)
                {
                    ok = false;
                    break;
                }
                R d1 = abs(c[0] - seg.a), d2 = abs(c[1] - seg.a);
                if (d1 > d2)
                    swap(d1, d2);
                res.emplace_back(d1, true);
                res.emplace_back(d2, false);
            }
            sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                 {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
            int cnt = 0;
            for (auto [_, v] : res)
            {
                if (v)
                    cnt++;
                else
                    cnt--;
                ok &= cnt > 0;
            }
            if (ok)
                chmin(ans, abs(seg.a - seg.b));
        }
        cout << ans << '\n';
    }
}
int main()
{
    cin.tie(0)->sync_with_stdio(0), cout.tie(0);
    cout << fixed << setprecision(20);
    int t = 1;
    // cin >> t;
    while (t--)
        solve();
}
accuracy: 1
time_ms: 580
memory_kb: 3996
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ld = long double;
#undef long
#define long long long
#define ll long
#define vec vector
#define rep(i, n) for (long i = 0; i < n; i++)
#define all(a) begin(a), end(a)
#define sz(a) (int)(a).size()
template <typename T>
bool chmin(T &x, T y)
{
    if (y < x)
    {
        x = y;
        return true;
    }
    return false;
}
template <typename T>
bool chmax(T &x, T y)
{
    if (x < y)
    {
        x = y;
        return true;
    }
    return false;
}
template <typename T>
ostream &operator<<(ostream &os, vector<T> a)
{
    const int n = a.size();
    rep(i, n)
    {
        os << a[i];
        if (i + 1 != n)
            os << " ";
    }
    return os;
}
using R = ld;
using point = complex<R>;
const R EPS(1e-10), PI(acosl(-1)), INF(1e18);
inline bool eq(const R &a, const R &b) { return fabsl(b - a) < EPS; }
inline int compare(const R &a, const R &b) { return eq(a, b) ? 0 : a < b ? -1
                                                                         : 1; }
struct circle
{
    point center;
    R radius;
    circle() = default;
    circle(const point &center, const R &radius) : center(center), radius(radius) {}
    friend std::ostream &operator<<(std::ostream &os, const circle &c)
    {
        return os << '(' << c.center << ", " << c.radius << ')';
    }
    friend std::istream &operator>>(std::istream &is, circle &c) { return is >> c.center >> c.radius; }
};
using points = vec<point>;
points crosspoints(const circle &c1, const circle &c2)
{
    R d = abs(c1.center - c2.center);
    if (compare(d, c1.radius + c2.radius) == 1)
        return points(0);
    if (compare(d, fabsl(c1.radius - c2.radius)) == -1)
        return points(0);
    bool one_crosspoint = false;
    if (eq(d, c1.radius + c2.radius) || eq(d, fabsl(c1.radius - c2.radius)))
        one_crosspoint = true;
    const R alpha =
        acosl((c1.radius * c1.radius + d * d - c2.radius * c2.radius) / (2 * c1.radius * d)); // cosine theorem
    const R beta = std::arg(c2.center - c1.center);
    if (one_crosspoint)
        return points{c1.center + std::polar(c1.radius, beta + alpha)};
    return points{c1.center + std::polar(c1.radius, beta + alpha), c1.center + std::polar(c1.radius, beta - alpha)};
}
struct segment;
struct line
{
    point a, b;
    line() = default;
    line(const point &a, const point &b) : a(a), b(b) {}
    // Ax + By + C = 0
    line(const R &A, const R &B, const R &C)
    {
        if (eq(A, 0))
        {
            assert(!eq(B, 0));
            a = point(0, -C / B), b = point(1, -(A + C) / B);
        }
        else
        {
            a = point(-C / A, 0), b = point(-(B + C) / A, 1);
        }
    }
    explicit line(const segment &seg);
    friend std::ostream &operator<<(std::ostream &os, const line &ln)
    {
        return os << '(' << ln.a << " -- " << ln.b << ')';
    }
    friend std::istream &operator>>(std::istream &is, line &a) { return is >> a.a >> a.b; }
};
struct segment
{
    point a, b;
    segment() = default;
    segment(const point &a, const point &b) : a(a), b(b) {}
    explicit segment(const line &ln) : a(ln.a), b(ln.b) {}
    friend std::ostream &operator<<(std::ostream &os, const segment &seg)
    {
        return os << '[' << seg.a << " -- " << seg.b << ']';
    }
    friend std::istream &operator>>(std::istream &is, segment &a) { return is >> a.a >> a.b; }
};
line::line(const segment &seg) : a(seg.a), b(seg.b) {}
R dot(const point &a, const point &b) { return real(a) * real(b) + imag(a) * imag(b); }
using arrow = point;

inline int sgn(const R &x) { return fabsl(x) < EPS ? 0 : (x < 0 ? -1 : 1); }
point projection(const line &l, const point &p)
{
    return l.a + (l.a - l.b) * dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
}
points crosspoints(const circle &c, const line &l)
{
    const point pr = projection(l, c.center);
    const R square = c.radius * c.radius - norm(pr - c.center);
    switch (sgn(square))
    {
    case 0:
        return points{pr};
    case -1:
        return points(0);
    }
    const arrow v = (l.b - l.a) / abs(l.b - l.a) * sqrtl(square);
    return points{pr - v, pr + v};
}
enum CCW
{
    ONLINE_FRONT = -2,     // a,b,c が一直線上
    CLOCKWISE = -1,        // c が a,b から時計回り側
    ON_SEGMENT = 0,        // a,c,b が一直線上
    COUNTER_CLOCKWISE = 1, // c が a,b から反時計回り側
    ONLINE_BACK = 2,       // c,a,b が一直線上
};
R cross(const point &a, const point &b) { return real(a) * imag(b) - imag(a) * real(b); }
int ccw(const point &a, point b, point c)
{
    b -= a, c -= a;
    const R crs_b_c = cross(b, c);
    if (crs_b_c > EPS)
        return CCW::COUNTER_CLOCKWISE;
    if (crs_b_c < -EPS)
        return CCW::CLOCKWISE;
    if (dot(b, c) < -EPS)
        return CCW::ONLINE_BACK;
    if (norm(b) + EPS < norm(c))
        return CCW::ONLINE_FRONT;
    return CCW::ON_SEGMENT;
}
int intersect(const segment &s, const point &p) { return int(ccw(s.a, s.b, p) == CCW::ON_SEGMENT); }
points crosspoints(const circle &c, const segment &s)
{
    points ret;
    for (const auto &pt : crosspoints(c, line(s)))
        if (intersect(s, pt))
            ret.push_back(pt);
    return ret;
}
void solve()
{
    while (true)
    {
        int n;
        cin >> n;
        if (n == 0)
            return;
        vec<circle> a(n);
        rep(i, n)
        {
            R x, y, r;
            cin >> x >> y >> r;
            a[i] = {{x, y}, r};
        }
        vec<points> b(n - 1);
        rep(i, n - 1)
        {
            b[i] = crosspoints(a[i], a[i + 1]);
            assert(sz(b[i]) == 2);
        }
        vec<vec<R>> d(n - 1, vec<R>(2, INF));
        R ans = INF;
        rep(i, n - 1)
        {
            rep(c0, 2)
            {
                { // 最初の円から
                    segment seg = {a[0].center, b[i][c0]};
                    bool ok = true;
                    vec<pair<R, bool>> res;
                    res.emplace_back(0, true);
                    res.emplace_back(a[0].radius, false);
                    res.emplace_back(abs(seg.a - seg.b), true);
                    for (int k = 1; k <= i; k++)
                    {
                        auto c = crosspoints(a[k], seg);
                        if (sz(c) != 2)
                        {
                            ok = false;
                            break;
                        }
                        R d1 = abs(c[0] - a[0].center), d2 = abs(c[1] - a[0].center);
                        if (d1 > d2)
                            swap(d1, d2);
                        res.emplace_back(d1, true);
                        res.emplace_back(d2, false);
                    }
                    sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                         {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
                    int cnt = 0;
                    for (auto [_, v] : res)
                    {
                        if (v)
                            cnt++;
                        else
                            cnt--;
                        ok &= cnt > 0;
                    }
                    if (ok)
                        chmin(d[i][c0], abs(a[0].center - b[i][c0]));
                }
                rep(j, i) rep(c1, 2)
                {
                    segment seg = {b[i][c0], b[j][c1]};
                    bool ok = true;
                    vec<pair<R, bool>> res;
                    res.emplace_back(abs(seg.a - seg.b), true);
                    for (int k = j + 1; k <= i; k++)
                    {
                        auto c = crosspoints(a[k], seg);
                        if (sz(c) != 2)
                        {
                            ok = false;
                            break;
                        }
                        R d1 = abs(c[0] - seg.a), d2 = abs(c[1] - seg.a);
                        if (d1 > d2)
                            swap(d1, d2);
                        res.emplace_back(d1, true);
                        res.emplace_back(d2, false);
                    }
                    sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                         {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
                    int cnt = 0;
                    for (auto [_, v] : res)
                    {
                        if (v)
                            cnt++;
                        else
                            cnt--;
                        ok &= cnt > 0;
                    }
                    if (ok)
                        chmin(d[i][c0], d[j][c1] + abs(seg.a - seg.b));
                }
            }
            R dm = abs(b[i][0] - b[i][1]);
            rep(c0, 2) chmin(d[i][c0], d[i][1 - c0] + dm);
            rep(c0, 2) // 最後の円へ
            {
                segment seg = {b[i][c0], a[n - 1].center};
                bool ok = true;
                vec<pair<R, bool>> res;
                res.emplace_back(abs(seg.a - seg.b) - a[n - 1].radius, true);
                for (int k = i + 1; k < n - 1; k++)
                {
                    auto c = crosspoints(a[k], seg);
                    if (sz(c) != 2)
                    {
                        ok = false;
                        break;
                    }
                    R d1 = abs(c[0] - seg.a), d2 = abs(c[1] - seg.a);
                    if (d1 > d2)
                        swap(d1, d2);
                    res.emplace_back(d1, true);
                    res.emplace_back(d2, false);
                }
                sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                     {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
                int cnt = 0;
                for (auto [_, v] : res)
                {
                    if (v)
                        cnt++;
                    else
                        cnt--;
                    ok &= cnt > 0;
                }
                if (ok)
                    chmin(ans, d[i][c0] + abs(seg.a - seg.b));
            }
        }
        {
            segment seg = {a[0].center, a[n - 1].center};
            bool ok = true;
            vec<pair<R, bool>> res;
            res.emplace_back(0, true);
            res.emplace_back(a[0].radius, false);
            res.emplace_back(abs(seg.a - seg.b) - a[n - 1].radius, true);
            for (int k = 1; k < n - 1; k++)
            {
                auto c = crosspoints(a[k], seg);
                if (sz(c) != 2)
                {
                    ok = false;
                    break;
                }
                R d1 = abs(c[0] - seg.a), d2 = abs(c[1] - seg.a);
                if (d1 > d2)
                    swap(d1, d2);
                res.emplace_back(d1, true);
                res.emplace_back(d2, false);
            }
            sort(all(res), [](pair<R, bool> &a, pair<R, bool> &b)
                 {
                        if(abs(a.first - b.first ) < EPS) return a.second ;
                        return a.first < b.first; });
            int cnt = 0;
            for (auto [_, v] : res)
            {
                if (v)
                    cnt++;
                else
                    cnt--;
                ok &= cnt > 0;
            }
            if (ok)
                chmin(ans, abs(seg.a - seg.b));
        }
        cout << ans << '\n';
    }
}
int main()
{
    cin.tie(0)->sync_with_stdio(0), cout.tie(0);
    cout << fixed << setprecision(20);
    int t = 1;
    // cin >> t;
    while (t--)
        solve();
}
accuracy: 1
time_ms: 580
memory_kb: 3884
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using P = pair<long long, long long>;
#define rep(i, a, b) for(long long i = (a); i < (b); ++i)
#define rrep(i, a, b) for(long long i = (a); i >= (b); --i)
constexpr long long inf = 4e18;
struct SetupIO {
    SetupIO() {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout << fixed << setprecision(30);
    }
} setup_io;
using Real = long double;
constexpr Real EPS = Real(1e-8), PI = 3.141592653589793238462643383279L;
int sign(const Real& r) {
    if(r <= -EPS) return -1;
    if(r >= +EPS) return +1;
    return 0;
}
bool eq(const Real& a, const Real& b) {
    return sign(a - b) == 0;
}
using Point = complex<Real>;
istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
ostream& operator<<(ostream& os, const Point& p) {
    return os << p.real() << " " << p.imag();
}
Point operator*(const Point& p, const Real& d) {
    return Point(p.real() * d, p.imag() * d);
}
Point operator/(const Point& p, const Real& d) {
    return Point(p.real() / d, p.imag() / d);
}
Point rot(const Point& p, const Real& theta) {
    return p * Point(cos(theta), sin(theta));
}
Real dot(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).real();
}
Real cross(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).imag();
}
Real dist(const Point& p1, const Point& p2) {
    return abs(p1 - p2);
}
bool comp_x(const Point& p1, const Point& p2) {
    return eq(p1.real(), p2.real()) ? p1.imag() < p2.imag() : p1.real() < p2.real();
}
bool comp_y(const Point& p1, const Point& p2) {
    return eq(p1.imag(), p2.imag()) ? p1.real() < p2.real() : p1.imag() < p2.imag();
}
bool comp_arg(const Point& p1, const Point& p2) {
    return arg(p1) < arg(p2);
}
int ccw(const Point& a, Point b, Point c) {
    b -= a;
    c -= a;
    if(sign(cross(b, c)) == 1) return 1;
    if(sign(cross(b, c)) == -1) return -1;
    if(sign(dot(b, c)) == -1) return +2;
    if(norm(b) < norm(c)) return -2;
    return 0;
}
Real closest_pair(vector<Point> ps) {
    if((int)ps.size() <= 1) return Real(1e18);
    sort(ps.begin(), ps.end(), comp_x);
    vector<Point> memo(ps.size());
    auto func = [&](const auto& func, const int l, const int r) -> Real {
        if(r - l <= 1) return Real(1e18);
        const int m = (l + r) >> 1;
        const Real x = ps[m].real();
        Real res = min(func(func, l, m), func(func, m, r));
        inplace_merge(ps.begin() + l, ps.begin() + m, ps.begin() + r, comp_y);
        int cnt = 0;
        for(int i = l; i < r; ++i) {
            if(abs(ps[i].real() - x) >= res) continue;
            for(int j = 0; j < cnt; ++j) {
                const Point d = ps[i] - memo[cnt - j - 1];
                if(d.imag() >= res) break;
                res = min(res, abs(d));
            }
            memo[cnt++] = ps[i];
        }
        return res;
    };
    return func(func, 0, (int)ps.size());
}
struct Line {
    Point a, b;
    Line() = default;
    Line(const Point& a, const Point& b)
        : a(a), b(b) {}
};
using Segment = Line;
bool is_parallel(const Line& a, const Line& b) {
    return eq(cross(a.b - a.a, b.b - b.a), 0.0);
}
bool is_orthogonal(const Line& a, const Line& b) {
    return eq(dot(a.b - a.a, b.b - b.a), 0.0);
}
Point projection(const Line& l, const Point& p) {
    Real t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + (l.b - l.a) * t;
}
Point reflection(const Line& l, const Point& p) {
    return p + (projection(l, p) - p) * 2.0;
}
bool is_intersect_lp(const Line& l, const Point& p) {
    return abs(ccw(l.a, l.b, p)) != 1;
}
bool is_intersect_sp(const Segment& s, const Point& p) {
    return ccw(s.a, s.b, p) == 0;
}
bool is_intersect_ll(const Line& l1, const Line& l2) {
    if(!eq(cross(l1.b - l1.a, l2.b - l2.a), 0.0)) return true;
    return eq(cross(l1.b - l1.a, l2.b - l1.a), 0.0);
}
bool is_intersect_ls(const Line& l, const Segment& s) {
    return sign(cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a)) <= 0;
}
bool is_intersect_sl(const Segment& s, const Line& l) {
    return is_intersect_ls(l, s);
}
bool is_intersect_ss(const Segment& s1, const Segment& s2) {
    if(ccw(s1.a, s1.b, s2.a) * ccw(s1.a, s1.b, s2.b) > 0) return false;
    return ccw(s2.a, s2.b, s1.a) * ccw(s2.a, s2.b, s1.b) <= 0;
}
vector<Point> intersection_ll(const Line& l1, const Line& l2) {
    vector<Point> res;
    if(!is_intersect_ll(l1, l2)) return res;
    Real a = cross(l1.b - l1.a, l2.b - l2.a);
    Real b = cross(l1.b - l1.a, l1.b - l2.a);
    if(eq(a, 0.0) and eq(b, 0.0)) {
        res.emplace_back(l2.a);
    } else {
        res.emplace_back(l2.a + (l2.b - l2.a) * b / a);
    }
    return res;
}
vector<Point> intersection_ss(const Segment& s1, const Segment& s2) {
    return is_intersect_ss(s1, s2) ? intersection_ll(Line(s1), Line(s2)) : vector<Point>();
}
Real dist_lp(const Line& l, const Point& p) {
    return abs(p - projection(l, p));
}
Real dist_sp(const Segment& s, const Point& p) {
    const Point h = projection(s, p);
    if(is_intersect_sp(s, h)) return abs(h - p);
    return min(abs(s.a - p), abs(s.b - p));
}
Real dist_ll(const Line& l1, const Line& l2) {
    if(is_intersect_ll(l1, l2)) return 0.0;
    return dist_lp(l1, l2.a);
}
Real dist_ss(const Segment& s1, const Segment& s2) {
    if(is_intersect_ss(s1, s2)) return 0.0;
    return min({dist_sp(s1, s2.a), dist_sp(s1, s2.b), dist_sp(s2, s1.a), dist_sp(s2, s1.b)});
}
Real dist_ls(const Line& l, const Segment& s) {
    if(is_intersect_ls(l, s)) return 0.0;
    return min(dist_lp(l, s.a), dist_lp(l, s.b));
}
Real dist_sl(const Segment& s, const Line& l) {
    return dist_ls(l, s);
}
struct Circle {
    Point p;
    Real r;
    Circle() = default;
    Circle(const Point& p, const Real& r)
        : p(p), r(r) {}
};
bool is_intersect_cp(const Circle& c, const Point& p) {
    return eq(abs(p - c.p), c.r);
}
bool is_intersect_cl(const Circle& c, const Line& l) {
    return sign(c.r - dist_lp(l, c.p)) >= 0;
}
int is_intersect_cc(Circle c1, Circle c2) {
    if(c1.r < c2.r) swap(c1, c2);
    const Real d = abs(c1.p - c2.p);
    const int a = sign(d - c1.r - c2.r);
    if(a >= 0) return a + 3;
    return sign(d - c1.r + c2.r) + 1;
}
vector<Point> intersection_cl(const Circle& c, const Line& l) {
    const Point h = projection(l, c.p);
    const Point e = (l.b - l.a) / abs(l.b - l.a);
    vector<Point> res;
    if(!is_intersect_cl(c, l)) return res;
    if(eq(dist_lp(l, c.p), c.r)) {
        res.emplace_back(h);
    } else {
        const Real b = sqrt(c.r * c.r - norm(h - c.p));
        res.emplace_back(h - e * b);
        res.emplace_back(h + e * b);
    }
    return res;
}
vector<Point> intersection_cs(const Circle& c, const Segment& s) {
    const vector<Point> cand = intersection_cl(c, Line(s));
    vector<Point> res;
    for(const Point& p : cand) {
        if(ccw(s.a, s.b, p) == 0) {
            res.emplace_back(p);
        }
    }
    return res;
}
vector<Point> intersection_cc(const Circle& c1, const Circle& c2) {
    const Real d = abs(c1.p - c2.p);
    const Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (Real(2.0) * c1.r * d));
    const Real t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
    vector<Point> res;
    if(is_intersect_cc(c1, c2) % 4 == 0) return res;
    if(eq(a, 0.0)) {
        res.emplace_back(c1.p + rot(Point(c1.r, 0.0), t));
    } else {
        res.emplace_back(c1.p + rot(Point(c1.r, 0.0), t + a));
        res.emplace_back(c1.p + rot(Point(c1.r, 0.0), t - a));
    }
    return res;
}
vector<Point> tangent_cp(const Circle& c, const Point& p) {
    return intersection_cc(c, Circle(p, sqrt(norm(p - c.p) - c.r * c.r)));
}
vector<Line> tangent_cc(Circle c1, Circle c2) {
    vector<Line> res;
    if(c1.r < c2.r) swap(c1, c2);
    const Real r = abs(c2.p - c1.p);
    if(eq(r, 0.0)) return res;
    const Point u = (c2.p - c1.p) / r;
    const Point v = rot(u, PI * 0.5);
    for(const Real s : {Real(1.0), Real(-1.0)}) {
        const Real h = (c1.r + c2.r * s) / r;
        if(eq(abs(h), Real(1.0))) {
            res.emplace_back(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
        } else if(abs(h) < Real(1.0)) {
            const Point uu = u * h, vv = v * sqrt(Real(1.0) - h * h);
            res.emplace_back(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
            res.emplace_back(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
        }
    }
    return res;
}
Circle inscribed_circle(const Point& a, const Point& b, const Point& c) {
    const Real A = abs(b - c), B = abs(c - a), C = abs(a - b);
    const Point x = Point((a * A + b * B + c * C) / (A + B + C));
    const Real r = dist_sp(Segment(a, b), x);
    return Circle(x, r);
}
Circle circumscribed_circle(const Point& a, const Point& b, const Point& c) {
    const Point m1((a + b) / 2.0), m2((b + c) / 2.0);
    const Point v((b - a).imag(), (a - b).real()), w((b - c).imag(), (c - b).real());
    const Line s(m1, Point(m1 + v)), t(m2, Point(m2 + w));
    const Point x = intersection_ll(s, t)[0];
    return Circle(x, abs(a - x));
}
Circle appollonius(const Point& p1, const Point& p2, const Real& a, const Real& b) {
    const Point q1 = (p1 * b + p2 * a) / (a + b), q2 = (-p1 * b + p2 * a) / (a - b);
    return Circle((q1 + q2) * 0.5, abs(q1 - q2) * 0.5);
}
Real area_cc(const Circle& c1, const Circle& c2) {
    const Real d = abs(c1.p - c2.p);
    if(c1.r + c2.r <= d + EPS) return 0.0;
    if(d <= abs(c1.r - c2.r) + EPS) {
        const Real r = min(c1.r, c2.r);
        return r * r * PI;
    }
    const Real rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (Real(2.0) * d);
    const Real theta = acos(rc / c1.r);
    const Real phi = acos((d - rc) / c2.r);
    return c1.r * c1.r * theta + c2.r * c2.r * phi - d * c1.r * sin(theta);
}
Real area_pc(const vector<Point>& ps, const Circle& c) {
    auto calc_element = [&](const Point& a, const Point& b, const Real& r, const bool triangle) -> Real {
        if(triangle) return cross(a, b) / 2.0;
        const Point tmp = b * Point(a.real(), -a.imag());
        const Real ang = atan2(tmp.imag(), tmp.real());
        return r * r * ang / 2.0;
    };
    auto cross_area = [&](const Circle& c, const Point& ia, const Point& ib) -> Real {
        const Point a = ia - c.p, b = ib - c.p;
        if(abs(a - b) < EPS) return 0;
        const bool isin_a = (abs(a) < c.r + EPS);
        const bool isin_b = (abs(b) < c.r + EPS);
        if(isin_a and isin_b) return calc_element(a, b, c.r, true);
        const Circle oc(Point(0.0, 0.0), c.r);
        const Segment seg(a, b);
        const vector<Point> cr = intersection_cs(oc, seg);
        if(cr.empty()) return calc_element(a, b, c.r, false);
        const Point s = cr[0], t = cr.back();
        return calc_element(s, t, c.r, true) + calc_element(a, s, c.r, isin_a) + calc_element(t, b, c.r, isin_b);
    };
    const int n = (int)ps.size();
    if(n < 3) return 0.0;
    Real S = 0;
    for(int i = 0; i < n; ++i) {
        S += cross_area(c, ps[i], ps[(i + 1) % n]);
    }
    return S;
}
template <typename T>
struct Edge {
    int from, to;
    T cost;
    int idx;
    Edge()
        : from(-1), to(-1), cost(-1), idx(-1) {}
    Edge(const int from, const int to, const T& cost = 1, const int idx = -1)
        : from(from), to(to), cost(cost), idx(idx) {}
    operator int() const {
        return to;
    }
};
template <typename T>
struct Graph {
    Graph(const int N)
        : n(N), es(0), g(N) {}
    int size() const {
        return n;
    }
    int edge_size() const {
        return es;
    }
    void add_edge(const int from, const int to, const T& cost = 1) {
        assert(0 <= from and from < n);
        assert(0 <= to and to < n);
        g[from].emplace_back(from, to, cost, es);
        g[to].emplace_back(to, from, cost, es++);
    }
    void add_directed_edge(const int from, const int to, const T& cost = 1) {
        assert(0 <= from and from < n);
        assert(0 <= to and to < n);
        g[from].emplace_back(from, to, cost, es++);
    }
    inline vector<Edge<T>>& operator[](const int& k) {
        assert(0 <= k and k < n);
        return g[k];
    }
    inline const vector<Edge<T>>& operator[](const int& k) const {
        assert(0 <= k and k < n);
        return g[k];
    }

   private:
    int n, es;
    vector<vector<Edge<T>>> g;
};
template <typename T>
using Edges = vector<Edge<T>>;
template <typename T>
vector<pair<T, int>> dijkstra(const Graph<T>& g, const int s = 0) {
    const int n = g.size();
    assert(0 <= s and s < n);
    vector<pair<T, int>> d(n, {numeric_limits<T>::max(), -1});
    priority_queue<pair<T, int>, vector<pair<T, int>>, greater<pair<T, int>>> pq;
    d[s] = {0, -1};
    pq.emplace(0, s);
    while(!pq.empty()) {
        const auto [dist, cur] = pq.top();
        pq.pop();
        if(d[cur].first < dist) continue;
        for(const Edge<T>& e : g[cur]) {
            if(d[e.to].first > d[cur].first + e.cost) {
                d[e.to] = {d[cur].first + e.cost, cur};
                pq.emplace(d[e.to].first, e.to);
            }
        }
    }
    return d;
}
int main(void) {
    while(1) {
        int n;
        cin >> n;
        if(n == 0) break;
        vector<Circle> c(n);
        rep(i, 0, n) {
            cin >> c[i].p >> c[i].r;
        }
        Graph<Real> g(2 * n);
        bool direct = true;
        rep(i, 0, n - 1) {
            vector<Point> u = intersection_cc(c[i], c[i + 1]);
            if(!is_intersect_ss(Segment(c[0].p, c[n - 1].p), Segment(u[0], u[1]))) {
                direct = false;
                break;
            }
        }
        if(direct) {
            g.add_edge(2 * n - 2, 2 * n - 1, dist(c[0].p, c[n - 1].p));
        }
        rep(i, 0, n - 1) {
            rep(i2, 0, 2) {
                Point s = c[0].p;
                Point t = intersection_cc(c[i], c[i + 1])[i2];
                bool flag = true;
                rep(k, 0, i) {
                    vector<Point> u = intersection_cc(c[k], c[k + 1]);
                    if(!is_intersect_ss(Segment(s, t), Segment(u[0], u[1]))) {
                        flag = false;
                        break;
                    }
                }
                if(flag) {
                    g.add_edge(2 * n - 2, i * 2 + i2, dist(s, t));
                }
            }
        }
        rep(i, 0, n - 1) {
            rep(i2, 0, 2) {
                Point s = c[n - 1].p;
                Point t = intersection_cc(c[i], c[i + 1])[i2];
                bool flag = true;
                rep(k, i + 1, n - 1) {
                    vector<Point> u = intersection_cc(c[k], c[k + 1]);
                    if(!is_intersect_ss(Segment(s, t), Segment(u[0], u[1]))) {
                        flag = false;
                        break;
                    }
                }
                if(flag) {
                    g.add_edge(i * 2 + i2, 2 * n - 1, dist(s, t));
                }
            }
        }
        rep(i, 0, n - 1) {
            rep(j, i + 1, n - 1) {
                rep(i2, 0, 2) {
                    rep(j2, 0, 2) {
                        Point s = intersection_cc(c[i], c[i + 1])[i2];
                        Point t = intersection_cc(c[j], c[j + 1])[j2];
                        bool flag = true;
                        rep(k, i + 1, j) {
                            vector<Point> u = intersection_cc(c[k], c[k + 1]);
                            if(!is_intersect_ss(Segment(s, t), Segment(u[0], u[1]))) {
                                flag = false;
                                break;
                            }
                        }
                        if(flag) {
                            g.add_edge(i * 2 + i2, j * 2 + j2, dist(s, t));
                        }
                    }
                }
            }
        }
        cout << dijkstra(g, 2 * n - 2)[2 * n - 1].first << '\n';
    }
}
accuracy: 1
time_ms: 580
memory_kb: 4312
final_rank: 19

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

using T = double;
const T eps = 1e-12;
using Point = complex<T>;
using Poly = vector<Point>;
#define X real()
#define Y imag()
template <typename T> inline bool eq(const T &a, const T &b) {
    return fabs(a - b) < eps;
}
bool cmp(const Point &a, const Point &b) {
    auto sub = [&](Point a) {
        return (a.Y < 0 ? -1 : (a.Y == 0 && a.X >= 0 ? 0 : 1));
    };
    if (sub(a) != sub(b))
        return sub(a) < sub(b);
    return a.Y * b.X < a.X * b.Y;
}
struct Line {
    Point a, b, dir;
    Line() {}
    Line(Point _a, Point _b) : a(_a), b(_b), dir(b - a) {}
    Line(T A, T B, T C) {
        if (eq(A, .0)) {
            a = Point(0, C / B), b = Point(1 / C / B);
        } else if (eq(B, .0)) {
            a = Point(C / A, 0), b = Point(C / A, 1);
        } else {
            a = Point(0, C / B), b = Point(C / A, 0);
        }
    }
};
struct Segment : Line {
    Segment() {}
    Segment(Point _a, Point _b) : Line(_a, _b) {}
};
struct Circle {
    Point p;
    T r;
    Circle() {}
    Circle(Point _p, T _r) : p(_p), r(_r) {}
};

istream &operator>>(istream &is, Point &p) {
    T x, y;
    is >> x >> y;
    p = Point(x, y);
    return is;
}
ostream &operator<<(ostream &os, Point &p) {
    os << fixed << setprecision(12) << p.X << ' ' << p.Y;
    return os;
}
Point unit(const Point &a) {
    return a / abs(a);
}
T dot(const Point &a, const Point &b) {
    return a.X * b.X + a.Y * b.Y;
}
T cross(const Point &a, const Point &b) {
    return a.X * b.Y - a.Y * b.X;
}
Point rot(const Point &a, const T &theta) {
    return Point(cos(theta) * a.X - sin(theta) * a.Y,
                 sin(theta) * a.X + cos(theta) * a.Y);
}
Point rot90(const Point &a) {
    return Point(-a.Y, a.X);
}
T arg(const Point &a, const Point &b, const Point &c) {
    double ret = acos(dot(a - b, c - b) / abs(a - b) / abs(c - b));
    if (cross(a - b, c - b) < 0)
        ret = -ret;
    return ret;
}

Point Projection(const Line &l, const Point &p) {
    T t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}
Point Projection(const Segment &l, const Point &p) {
    T t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}
Point Reflection(const Line &l, const Point &p) {
    return p + (Projection(l, p) - p) * 2.;
}
int ccw(const Point &a, Point b, Point c) {
    b -= a;
    c -= a;
    if (cross(b, c) > eps)
        return 1; // ccw

    if (cross(b, c) < -eps)
        return -1; // cw

    if (dot(b, c) < 0)
        return 2; // c,a,b

    if (norm(b) < norm(c))
        return -2; // a,b,c

    return 0;      // a,c,b

}
bool isOrthogonal(const Line &a, const Line &b) {
    return eq(dot(a.b - a.a, b.b - b.a), .0);
}
bool isParallel(const Line &a, const Line &b) {
    return eq(cross(a.b - a.a, b.b - b.a), .0);
}
bool isIntersect(const Segment &a, const Segment &b) {
    return ccw(a.a, a.b, b.a) * ccw(a.a, a.b, b.b) <= 0 and
           ccw(b.a, b.b, a.a) * ccw(b.a, b.b, a.b) <= 0;
}
int isIntersect(const Circle &a, const Circle &b) {
    T d = abs(a.p - b.p);
    if (d > a.r + b.r + eps)
        return 4;
    if (eq(d, a.r + b.r))
        return 3;
    if (eq(d, abs(a.r - b.r)))
        return 1;
    if (d < abs(a.r - b.r) - eps)
        return 0;
    return 2;
}
T Dist(const Line &a, const Point &b) {
    Point c = Projection(a, b);
    return abs(c - b);
}
T Dist(const Segment &a, const Point &b) {
    if (dot(a.b - a.a, b - a.a) < eps)
        return abs(b - a.a);
    if (dot(a.a - a.b, b - a.b) < eps)
        return abs(b - a.b);
    return abs(cross(a.b - a.a, b - a.a)) / abs(a.b - a.a);
}
T Dist(const Segment &a, const Segment &b) {
    if (isIntersect(a, b))
        return .0;
    T res = min({Dist(a, b.a), Dist(a, b.b), Dist(b, a.a), Dist(b, a.b)});
    return res;
}
Point Intersection(const Line &a, const Line &b) {
    T d1 = cross(a.b - a.a, b.b - b.a);
    T d2 = cross(a.b - a.a, a.b - b.a);
    if (eq(d1, 0.) and eq(d2, 0.))
        return b.a;
    return b.a + (b.b - b.a) * (d2 / d1);
}
Poly Intersection(const Circle &a, const Line &b) {
    Poly res;
    T d = Dist(b, a.p);
    if (d > a.r + eps)
        return res;
    Point h = Projection(b, a.p);
    if (eq(d, a.r)) {
        res.push_back(h);
        return res;
    }
    Point e = unit(b.b - b.a);
    T ph = sqrt(a.r * a.r - d * d);
    res.push_back(h - e * ph);
    res.push_back(h + e * ph);
    return res;
}
Poly Intersection(const Circle &a, const Segment &b) {
    Line c(b.a, b.b);
    Poly sub = Intersection(a, c);
    double xmi = min(b.a.X, b.b.X), xma = max(b.a.X, b.b.X);
    double ymi = min(b.a.Y, b.b.Y), yma = max(b.a.Y, b.b.Y);
    Poly res;
    rep(i, 0, sub.size()) {
        if (xmi <= sub[i].X + eps and sub[i].X - eps <= xma and
            ymi <= sub[i].Y + eps and sub[i].Y - eps <= yma) {
            res.push_back(sub[i]);
        }
    }
    return res;
}
Poly Intersection(const Circle &a, const Circle &b) {
    Poly res;
    int mode = isIntersect(a, b);
    T d = abs(a.p - b.p);
    if (mode == 4 or mode == 0)
        return res;
    if (mode == 3) {
        T t = a.r / (a.r + b.r);
        res.push_back(a.p + (b.p - a.p) * t);
        return res;
    }
    if (mode == 1) {
        if (b.r < a.r - eps) {
            res.push_back(a.p + (b.p - a.p) * (a.r / d));
        } else {
            res.push_back(b.p + (a.p - b.p) * (b.r / d));
        }
        return res;
    }
    T rc = (a.r * a.r + d * d - b.r * b.r) / d / 2.;
    T rs = sqrt(a.r * a.r - rc * rc);
    if (a.r - abs(rc) < eps)
        rs = 0;
    Point e = unit(b.p - a.p);
    res.push_back(a.p + rc * e + rs * e * Point(0, 1));
    res.push_back(a.p + rc * e + rs * e * Point(0, -1));
    return res;
}
Poly HalfplaneIntersection(vector<Line> &H) {
    sort(ALL(H), [&](Line &l1, Line &l2) { return cmp(l1.dir, l2.dir); });
    auto outside = [&](Line &L, Point p) -> bool {
        return cross(L.dir, p - L.a) < -eps;
    };
    deque<Line> deq;
    int sz = 0;
    rep(i, 0, SZ(H)) {
        while (sz > 1 and
               outside(H[i], Intersection(deq[sz - 1], deq[sz - 2]))) {
            deq.pop_back();
            sz--;
        }
        while (sz > 1 and outside(H[i], Intersection(deq[0], deq[1]))) {
            deq.pop_front();
            sz--;
        }
        if (sz > 0 and fabs(cross(H[i].dir, deq[sz - 1].dir)) < eps) {
            if (dot(H[i].dir, deq[sz - 1].dir) < 0) {
                return {};
            }
            if (outside(H[i], deq[sz - 1].a)) {
                deq.pop_back();
                sz--;
            } else
                continue;
        }
        deq.push_back(H[i]);
        sz++;
    }

    while (sz > 2 and outside(deq[0], Intersection(deq[sz - 1], deq[sz - 2]))) {
        deq.pop_back();
        sz--;
    }
    while (sz > 2 and outside(deq[sz - 1], Intersection(deq[0], deq[1]))) {
        deq.pop_front();
        sz--;
    }
    if (sz < 3)
        return {};
    deq.push_back(deq.front());
    Poly ret;
    rep(i, 0, sz) ret.push_back(Intersection(deq[i], deq[i + 1]));
    return ret;
}

T Area(const Poly &a) {
    T res = 0;
    int n = a.size();
    rep(i, 0, n) res += cross(a[i], a[(i + 1) % n]);
    return fabs(res / 2.);
}
T Area(const Poly &a, const Circle &b) {
    int n = a.size();
    if (n < 3)
        return .0;
    auto rec = [&](auto self, const Circle &c, const Point &p1,
                   const Point &p2) {
        Point va = c.p - p1, vb = c.p - p2;
        T f = cross(va, vb), res = .0;
        if (eq(f, .0))
            return res;
        if (max(abs(va), abs(vb)) < c.r + eps)
            return f;
        if (Dist(Segment(p1, p2), c.p) > c.r - eps)
            return c.r * c.r * arg(vb * conj(va));
        auto u = Intersection(c, Segment(p1, p2));
        Poly sub;
        sub.push_back(p1);
        for (auto &x : u)
            sub.push_back(x);
        sub.push_back(p2);
        rep(i, 0, sub.size() - 1) res += self(self, c, sub[i], sub[i + 1]);
        return res;
    };
    T res = .0;
    rep(i, 0, n) res += rec(rec, b, a[i], a[(i + 1) % n]);
    return fabs(res / 2.);
}
T Area(const Circle &a, const Circle &b) {
    T d = abs(a.p - b.p);
    if (d >= a.r + b.r - eps)
        return .0;
    if (d <= abs(a.r - b.r) + eps) {
        T r = min(a.r, b.r);
        return M_PI * r * r;
    }
    T ath = acos((a.r * a.r + d * d - b.r * b.r) / d / a.r / 2.);
    T res = a.r * a.r * (ath - sin(ath * 2) / 2.);
    T bth = acos((b.r * b.r + d * d - a.r * a.r) / d / b.r / 2.);
    res += b.r * b.r * (bth - sin(bth * 2) / 2.);
    return fabs(res);
}
bool isConvex(const Poly &a) {
    int n = a.size();
    int cur, pre, nxt;
    rep(i, 0, n) {
        pre = (i - 1 + n) % n;
        nxt = (i + 1) % n;
        cur = i;
        if (ccw(a[pre], a[cur], a[nxt]) == -1)
            return 0;
    }
    return 1;
}
int isContained(const Poly &a,
                const Point &b) { // 0:not contain,1:on edge,2:contain

    bool res = 0;
    int n = a.size();
    rep(i, 0, n) {
        Point p = a[i] - b, q = a[(i + 1) % n] - b;
        if (p.Y > q.Y)
            swap(p, q);
        if (p.Y < eps and eps < q.Y and cross(p, q) > eps)
            res ^= 1;
        if (eq(cross(p, q), .0) and dot(p, q) < eps)
            return 1;
    }
    return (res ? 2 : 0);
}
Poly ConvexHull(Poly &a) {
    sort(ALL(a), [](const Point &p, const Point &q) {
        return (eq(p.Y, q.Y) ? p.X < q.X : p.Y < q.Y);
    });
    a.erase(unique(ALL(a)), a.end());
    int n = a.size(), k = 0;
    Poly res(n * 2);
    for (int i = 0; i < n; res[k++] = a[i++]) {
        while (k >= 2 and
               cross(res[k - 1] - res[k - 2], a[i] - res[k - 1]) < eps)
            k--;
    }
    for (int i = n - 2, t = k + 1; i >= 0; res[k++] = a[i--]) {
        while (k >= t and
               cross(res[k - 1] - res[k - 2], a[i] - res[k - 1]) < eps)
            k--;
    }
    res.resize(k - 1);
    return res;
}
T Diam(const Poly &a) {
    int n = a.size();
    int x = 0, y = 0;
    rep(i, 1, n) {
        if (a[i].Y > a[x].Y)
            x = i;
        if (a[i].Y < a[y].Y)
            y = i;
    }
    T res = abs(a[x] - a[y]);
    int i = x, j = y;
    do {
        if (cross(a[(i + 1) % n] - a[i], a[(j + 1) % n] - a[j]) < 0)
            i = (i + 1) % n;
        else
            j = (j + 1) % n;
        chmax(res, abs(a[i] - a[j]));
    } while (i != x or j != y);
    return res;
}
Poly Cut(const Poly &a, const Line &l) {
    int n = a.size();
    Poly res;
    rep(i, 0, n) {
        Point p = a[i], q = a[(i + 1) % n];
        if (ccw(l.a, l.b, p) != -1)
            res.push_back(p);
        if (ccw(l.a, l.b, p) * ccw(l.a, l.b, q) < 0)
            res.push_back(Intersection(Line(p, q), l));
    }
    return res;
}

T Closest(Poly &a) {
    int n = a.size();
    if (n <= 1)
        return 0;
    sort(ALL(a), [&](Point a, Point b) {
        return (eq(a.X, b.X) ? a.Y < b.Y : a.X < b.X);
    });
    Poly buf(n);
    auto rec = [&](auto self, int lb, int rb) -> T {
        if (rb - lb <= 1)
            return (T)INF;
        int mid = (lb + rb) >> 1;
        auto x = a[mid].X;
        T res = min(self(self, lb, mid), self(self, mid, rb));
        inplace_merge(a.begin() + lb, a.begin() + mid, a.begin() + rb,
                      [&](auto p, auto q) { return p.Y < q.Y; });
        int ptr = 0;
        rep(i, lb, rb) {
            if (abs(a[i].X - x) >= res)
                continue;
            rep(j, 0, ptr) {
                auto sub = a[i] - buf[ptr - 1 - j];
                if (sub.Y >= res)
                    break;
                chmin(res, abs(sub));
            }
            buf[ptr++] = a[i];
        }
        return res;
    };
    return rec(rec, 0, n);
}

Circle Incircle(const Point &a, const Point &b, const Point &c) {
    T A = abs(b - c), B = abs(c - a), C = abs(a - b);
    Point p(A * a.X + B * b.X + C * c.X, A * a.Y + B * b.Y + C * c.Y);
    p /= (A + B + C);
    T r = Dist(Line(a, b), p);
    return Circle(p, r);
}
Circle Circumcircle(const Point &a, const Point &b, const Point &c) {
    Line l1((a + b) / 2., (a + b) / 2. + (b - a) * Point(0, 1));
    Line l2((b + c) / 2., (b + c) / 2. + (c - b) * Point(0, 1));
    Point p = Intersection(l1, l2);
    return Circle(p, abs(p - a));
}
Poly tangent(const Point &a, const Circle &b) {
    return Intersection(b, Circle(a, sqrt(norm(b.p - a) - b.r * b.r)));
}
vector<Line> tangent(const Circle &a, const Circle &b) {
    vector<Line> res;
    T d = abs(a.p - b.p);
    if (eq(d, 0.))
        return res;
    Point u = unit(b.p - a.p);
    Point v = u * Point(0, 1);
    for (int t : {-1, 1}) {
        T h = (a.r + b.r * t) / d;
        if (eq(h * h, 1.)) {
            res.push_back(Line(a.p + (h > 0 ? u : -u) * a.r,
                               a.p + (h > 0 ? u : -u) * a.r + v));
        } else if (1 > h * h) {
            Point U = u * h, V = v * sqrt(1 - h * h);
            res.push_back(Line(a.p + (U + V) * a.r, b.p - (U + V) * (b.r * t)));
            res.push_back(Line(a.p + (U - V) * a.r, b.p - (U - V) * (b.r * t)));
        }
    }
    return res;
}

/**
 * @brief Geometry
 */

int main() {
while(1) {
    int N;
    cin >> N;
    if (N == 0) return 0;
    vector<Circle> C(N);
    rep(i,0,N) {
        double PX, PY, R;
        cin >> PX >> PY >> R;
        Point p(PX,PY);
        Circle c(p,R);
        C[i] = c;
    }
    vector<Poly> V(N-1);
    vector<Segment> S(N-1);
    rep(i,0,N-1) {
        V[i] = Intersection(C[i], C[i+1]);
        Segment s(V[i][0], V[i][1]);
        S[i] = s;
    }
    vector<vector<double>> D(N-1,vector<double>(2,inf));
    bool check;
    rep(i,0,N-1) {
        rep(j,0,2) {
            Point p = V[i][j];
            {   
                check = true;
                Segment s(p,C[0].p);
                rep(k,0,i) {
                    if (!isIntersect(s,S[k])) check = false;
                }
                if (check) chmin(D[i][j], abs(p-C[0].p));
            }
            rep(k,0,i) {
                rep(l,0,2) {
                    check = true;
                    Segment s(p,V[k][l]);
                    rep(m,k+1,i) {
                        if (!isIntersect(s,S[m])) check = false;
                    }
                    if (check) chmin(D[i][j], D[k][l] + abs(p-V[k][l]));
                }
            }
        }
    }
    double ANS = inf;
    {   check = true;
        Segment s(C[0].p, C[N-1].p);
        rep(i,0,N-1) {
            if (!isIntersect(s,S[i])) check = false;
        }
        if (check) chmin(ANS, abs(C[N-1].p-C[0].p));
    }
    rep(i,0,N-1) {
        rep(j,0,2) {
            check = true;
            Segment s(V[i][j], C[N-1].p);
            rep(k,i+1,N-1) {
                if (!isIntersect(s,S[k])) check = false;
            }
            if (check) chmin(ANS, D[i][j] + abs(C[N-1].p-V[i][j]));
        }
    }
    print(ANS);
}
}
accuracy: 1
time_ms: 60
memory_kb: 3568
final_rank: 1

code_snippet: #include <iostream>
#include <vector>
#include <complex>
#include <cmath>
#include <limits>
#include <queue>
#include <iomanip>

using namespace std;
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define rrep(i, n) for (int i = 1; i <= (n); ++i)
#define srep(i, s, t) for (int i = s; i < (t); ++i)

template<class T, class U> bool chmin(T& a, const U& b){ if(a <= b) return 0; a = b; return 1; }
template<class T, class U> bool chmax(T& a, const U& b){ if(a >= b) return 0; a = b; return 1; }

using ll = long long; using ld = long double;
using vi = vector<int>; using vd = vector<double>;
using point = complex<double>;
using vp = vector<point>;
using ppp = pair<point, point>;

const double INF = numeric_limits<double>::infinity();
const ll LINF = 0x1fffffffffffffff;
const double EPS = 1e-10;

class Circle {
public:
    point ctr;
    double r;

    Circle() : ctr(0, 0), r(0) {}
    Circle(double x, double y, double r) : ctr(x, y), r(r) {}
};

// 2点で交わる前提で、２つの円の交点を返す
ppp intersection(const Circle& p, const Circle& q) {
    double d = abs(q.ctr - p.ctr); // 中心間距離d
    double y = (p.r * p.r - q.r * q.r + d * d) / (2 * d);
    double x = sqrt(p.r * p.r - y * y);

    point p1 = p.ctr + (y / d) * (q.ctr - p.ctr); // 交点の中点
    point offset = point((q.ctr.imag() - p.ctr.imag()) * (x / d), (q.ctr.real() - p.ctr.real()) * (-x / d));
    point p2 = p1 + offset;
    point p3 = p1 - offset;
    return make_pair(p2, p3);
}

// 線分(注:Lineは日本語で直線)
struct Line { point p, q; Line(point p, point q) : p(p), q(q) {} };

// 外積
double cross(point p, point q) { return imag(conj(p) * q); }

// 交差判定
bool ssIntersect(Line a, Line b) {
    if (abs(imag((a.q - a.p) / (b.q - b.p))) < EPS) return false;
    return cross(a.q - a.p, b.p - a.p) * cross(a.q - a.p, b.q - a.p) < 0 &&
           cross(b.q - b.p, a.p - b.p) * cross(b.q - b.p, a.q - b.p) < 0;
}

class WeightedEdge {
public:
    int to;
    double cost;

    WeightedEdge() {}
    WeightedEdge(int to, double cost) : to(to), cost(cost){}
};

class WeightedGraph {
    using E = WeightedEdge;
private:
    vector<vector<E>> g;

public:
    WeightedGraph(int n) : g(n) {}

    const vector<E>& operator[](int at) const { return g[at]; }

    int size() const { return g.size(); }

    void addEdge(int a, int b, double weight) {
        g[a].emplace_back(b, weight);
        g[b].emplace_back(a, weight);
    }

    void addDirectedEdge(int from, int to, double weight) {
        g[from].emplace_back(to, weight);
    }

    void inputGraph(int m) {
        while (m-- > 0) {
            int a, b;
            double c;
            cin >> a >> b >> c;
            g[a].emplace_back(b, c);
            // g[b].emplace_back(a, c);
        }
    }
    // dijkstra
    vd Dijkstra(int start) const {
        int n = g.size();
        vd cost(n, INF);
        vector<bool> used(n);
        priority_queue<pair<double, int>, vector<pair<double, int>>, greater<pair<double, int>>> q;
        cost[start] = 0;
        q.emplace(0, start);
        while (!q.empty()) {
            int at = q.top().second;
            q.pop();
            if (used[at]) continue;
            used[at] = true;
            for (auto& e : g[at]) {
                int to = e.to;
                double c = e.cost;
                if (chmin(cost[to], cost[at] + c)) {
                    q.emplace(cost[to], to);
                }
            }
        }
        return cost;
    }
};

int main() {
    int n; 
    while(cin >> n, n) {
        vp ps; //points
        vector<Circle> cs(n); //circles
        rep(i, n) {
            double x, y, r;
            cin >> x >> y >> r;
            cs[i] = Circle(x, y, r);
        }
        ps.push_back(cs[0].ctr);
        rep(i, n - 1) {
            ppp is = intersection(cs[i], cs[i + 1]);
            ps.push_back(is.first);
            ps.push_back(is.second);
        }
        ps.push_back(cs.back().ctr); // ここまでで∑2nの点が格納された
        WeightedGraph g(2 * n); // 頂点数2nの重み付きグラフ
        rep(i, 2 * n) srep(j, i + 1, 2 * n) {
            bool flag = true;
            srep(k, (i + 1) / 2 + 1, (j + 1) / 2) {
                if (!ssIntersect(Line(ps[i], ps[j]), Line(ps[2 * k - 1], ps[2 * k]))){ flag = false; }
            }
            if (flag) { g.addEdge(i, j, abs(ps[i] - ps[j])); } 
        }
        vd dist = g.Dijkstra(0);
        cout << fixed << setprecision(6) << dist.back() << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 130
memory_kb: 3748
final_rank: 5

code_snippet: // #pragma GCC target("avx2")
#pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
// #include<sys/time.h>
// #include<atcoder/modint>

using namespace std;

// using P = pair<int, int>;

// const int M = 998244353;
// const long long LM = 1LL << 60;

using fltype = double;
using itype = long long;
const fltype eps = 1e-8;
const fltype inf = 1e20;
using Point = complex<fltype>;
using Line = pair<Point, Point>;
using Circle = pair<pair<itype, itype>, itype>;


vector<fltype> crossparams(const Circle& c, const Line& l) {
    fltype x0 = real(l.first);
    fltype y0 = imag(l.first);
    fltype x = c.first.first - x0;
    fltype y = c.first.second - y0;
    fltype a = real(l.second) - x0;
    fltype b = imag(l.second) - y0;
    fltype d = a * a + b * b;
    a /= d;
    b /= d;
    fltype D = c.second * (fltype)c.second / d - (a * y - b * x) * (a * y - b * x);
    if (D < eps * eps) {
        return {};
    }
    else {
        fltype p = sqrt(D);
        return { a * x + b * y - p, a * x + b * y + p };
    }
}

vector<Point> crosspoints(const Circle& a, const Circle& b) {
    {
        itype d = (b.first.first - a.first.first) * (b.first.first - a.first.first) + (b.first.second - a.first.second) * (b.first.second - a.first.second);
        itype rp = (a.second + b.second) * (a.second + b.second);
        itype rm = (a.second - b.second) * (a.second - b.second);
        if (d > rp || rm > d) {
            return {};
        }
    }
    {
        fltype x0 = a.first.first;
        fltype y0 = a.first.second;
        fltype x = b.first.first - x0;
        fltype y = b.first.second - y0;
        fltype r1 = a.second * (fltype)a.second / (x * x + y * y);
        fltype r2 = b.second * (fltype)b.second / (x * x + y * y);
        fltype p = (1 + r1 - r2) / 2;
        fltype D = r1 - p * p;
        if (D < eps * eps) {
            return {
                Point(x0 + p * x, y0 + p * y),
            };
        }
        else {
            fltype q = sqrt(D);
            return {
                Point(x0 + p * x + q * y, y0 + p * y - q * x),
                Point(x0 + p * x - q * y, y0 + p * y + q * x),
            };
        }
    }
}

bool through(const vector<Circle>& cs, const Line& l) {
    if (abs(l.second - l.first) < eps) {
        return true;
    }
    vector<pair<fltype, int>> ts;
    for (const auto& c : cs) {
        auto r = crossparams(c, l);
        if (r.size() == 2) {
            ts.emplace_back(r[0], +1);
            ts.emplace_back(r[1], -1);
        }
    }
    sort(ts.begin(), ts.end());
    int c = 0;
    fltype s = 0;
    for (int i = 0; i + 1 < (int)ts.size(); ++i) {
        c += ts[i].second;
        if (c <= 0) {
            s += max(min(ts[i + 1].first, (fltype)1) - max(ts[i].first, (fltype)0), (fltype)(0));
        }
    }
    return s < eps;
}


int main() {
    cin.tie(0);
    ios::sync_with_stdio(0);
    cout << fixed << setprecision(16);
    while (true) {
        int n;
        cin >> n;
        if (n == 0) {
            return 0;
        }
        vector<Circle> cs(n);
        for (int i = 0; i < n; ++i) {
            cin >> cs[i].first.first >> cs[i].first.second >> cs[i].second;
        }
        vector<Point> ps;
        ps.emplace_back((fltype)cs.front().first.first, (fltype)cs.front().first.second);
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                for (auto& p : crosspoints(cs[i], cs[j])) {
                    ps.push_back(p);
                }
            }
        }
        ps.emplace_back((fltype)cs.back().first.first, (fltype)cs.back().first.second);
        int m = ps.size();
        vector<fltype> dist(m, inf * 2);
        vector<bool> used(m);
        dist[0] = 0;
        while (true) {
            int nex = -1;
            for (int i = 0; i < m; ++i) {
                if (!used[i] && (nex == -1 || dist[i] < dist[nex])) {
                    nex = i;
                }
            }
            if (nex == -1 || dist[nex] > inf) {
                break;
            }
            used[nex] = true;
            for (int i = max(0, nex - 1); i < m; ++i) {
                if (nex != i && through(cs, { ps[nex], ps[i] })) {
                    dist[i] = min(dist[i], dist[nex] + abs(ps[i] - ps[nex]));
                }
            }
        }
        if (dist.back() > inf) {
            cout << "impossible\n";
        }
        else {
            cout << dist.back() << '\n';
        }
    }
    return 0;
}
accuracy: 1
time_ms: 340
memory_kb: 3532
final_rank: 3

code_snippet: // #pragma GCC target("avx2")
#pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
// #include<sys/time.h>
// #include<atcoder/modint>

using namespace std;

// using P = pair<int, int>;

// const int M = 998244353;
// const long long LM = 1LL << 60;

using fltype = double;
using itype = long long;
const fltype eps = 1e-8;
const fltype inf = 1e20;
using Point = complex<fltype>;
using Line = pair<Point, Point>;
using Circle = pair<pair<itype, itype>, itype>;


vector<fltype> crossparams(const Circle& c, const Line& l) {
    fltype x0 = real(l.first);
    fltype y0 = imag(l.first);
    fltype x = c.first.first - x0;
    fltype y = c.first.second - y0;
    fltype a = real(l.second) - x0;
    fltype b = imag(l.second) - y0;
    fltype d = a * a + b * b;
    a /= d;
    b /= d;
    fltype D = c.second * (fltype)c.second / d - (a * y - b * x) * (a * y - b * x);
    if (D < eps * eps) {
        return {};
    }
    else {
        fltype p = sqrt(D);
        return { a * x + b * y - p, a * x + b * y + p };
    }
}

vector<Point> crosspoints(const Circle& a, const Circle& b) {
    {
        itype d = (b.first.first - a.first.first) * (b.first.first - a.first.first) + (b.first.second - a.first.second) * (b.first.second - a.first.second);
        itype rp = (a.second + b.second) * (a.second + b.second);
        itype rm = (a.second - b.second) * (a.second - b.second);
        if (d > rp || rm > d) {
            return {};
        }
    }
    {
        fltype x0 = a.first.first;
        fltype y0 = a.first.second;
        fltype x = b.first.first - x0;
        fltype y = b.first.second - y0;
        fltype r1 = a.second * (fltype)a.second / (x * x + y * y);
        fltype r2 = b.second * (fltype)b.second / (x * x + y * y);
        fltype p = (1 + r1 - r2) / 2;
        fltype D = r1 - p * p;
        if (D < eps * eps) {
            return {
                Point(x0 + p * x, y0 + p * y),
            };
        }
        else {
            fltype q = sqrt(D);
            return {
                Point(x0 + p * x + q * y, y0 + p * y - q * x),
                Point(x0 + p * x - q * y, y0 + p * y + q * x),
            };
        }
    }
}

bool through(const vector<Circle>& cs, const Line& l) {
    if (abs(l.second - l.first) < eps) {
        return true;
    }
    vector<pair<fltype, int>> ts;
    for (const auto& c : cs) {
        auto r = crossparams(c, l);
        if (r.size() == 2) {
            ts.emplace_back(r[0], +1);
            ts.emplace_back(r[1], -1);
        }
    }
    sort(ts.begin(), ts.end());
    int c = 0;
    fltype s = 0;
    for (int i = 0; i + 1 < (int)ts.size(); ++i) {
        c += ts[i].second;
        if (c <= 0) {
            s += max(min(ts[i + 1].first, (fltype)1) - max(ts[i].first, (fltype)0), (fltype)(0));
        }
    }
    return s < eps;
}


int main() {
    cin.tie(0);
    ios::sync_with_stdio(0);
    cout << fixed << setprecision(16);
    while (true) {
        int n;
        cin >> n;
        if (n == 0) {
            return 0;
        }
        vector<Circle> cs(n);
        for (int i = 0; i < n; ++i) {
            cin >> cs[i].first.first >> cs[i].first.second >> cs[i].second;
        }
        vector<Point> ps;
        ps.emplace_back((fltype)cs.front().first.first, (fltype)cs.front().first.second);
        ps.emplace_back((fltype)cs.back().first.first, (fltype)cs.back().first.second);
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                for (auto& p : crosspoints(cs[i], cs[j])) {
                    ps.push_back(p);
                }
            }
        }
        int m = ps.size();
        vector<fltype> dist(m, inf * 2);
        vector<bool> used(m);
        dist[0] = 0;
        while (!used[1]) {
            int nex = -1;
            for (int i = 0; i < m; ++i) {
                if (!used[i] && (nex == -1 || dist[i] < dist[nex])) {
                    nex = i;
                }
            }
            if (nex == -1 || dist[nex] > inf) {
                break;
            }
            used[nex] = true;
            for (int i = 0; i < m; ++i) {
                if (nex != i && through(cs, { ps[nex], ps[i] })) {
                    dist[i] = min(dist[i], dist[nex] + abs(ps[i] - ps[nex]));
                }
            }
        }
        if (dist[1] > inf) {
            cout << "impossible\n";
        }
        else {
            cout << dist[1] << '\n';
        }
    }
    return 0;
}
accuracy: 1
time_ms: 700
memory_kb: 3548
final_rank: 10

code_snippet: // #pragma GCC target("avx2")
#pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
// #include<sys/time.h>
// #include<atcoder/modint>

using namespace std;

// using P = pair<int, int>;

// const int M = 998244353;
// const long long LM = 1LL << 60;

using fltype = double;
using itype = long long;
const fltype eps = 1e-8;
const fltype inf = 1e20;
using Point = complex<fltype>;
using Line = pair<Point, Point>;
using Circle = pair<pair<itype, itype>, itype>;


vector<fltype> crossparams(const Circle& c, const Line& l) {
    fltype x0 = real(l.first);
    fltype y0 = imag(l.first);
    fltype x = c.first.first - x0;
    fltype y = c.first.second - y0;
    fltype a = real(l.second) - x0;
    fltype b = imag(l.second) - y0;
    fltype d = a * a + b * b;
    a /= d;
    b /= d;
    fltype D = c.second * (fltype)c.second / d - (a * y - b * x) * (a * y - b * x);
    if (D < eps * eps) {
        return {};
    }
    else {
        fltype p = sqrt(D);
        return { a * x + b * y - p, a * x + b * y + p };
    }
}

vector<Point> crosspoints(const Circle& a, const Circle& b) {
    {
        itype d = (b.first.first - a.first.first) * (b.first.first - a.first.first) + (b.first.second - a.first.second) * (b.first.second - a.first.second);
        itype rp = (a.second + b.second) * (a.second + b.second);
        itype rm = (a.second - b.second) * (a.second - b.second);
        if (d > rp || rm > d) {
            return {};
        }
    }
    {
        fltype x0 = a.first.first;
        fltype y0 = a.first.second;
        fltype x = b.first.first - x0;
        fltype y = b.first.second - y0;
        fltype r1 = a.second * (fltype)a.second / (x * x + y * y);
        fltype r2 = b.second * (fltype)b.second / (x * x + y * y);
        fltype p = (1 + r1 - r2) / 2;
        fltype D = r1 - p * p;
        if (D < eps * eps) {
            return {
                Point(x0 + p * x, y0 + p * y),
            };
        }
        else {
            fltype q = sqrt(D);
            return {
                Point(x0 + p * x + q * y, y0 + p * y - q * x),
                Point(x0 + p * x - q * y, y0 + p * y + q * x),
            };
        }
    }
}

bool through(const vector<Circle>& cs, const Line& l) {
    if (abs(l.second - l.first) < eps) {
        return true;
    }
    vector<pair<fltype, int>> ts;
    for (const auto& c : cs) {
        auto r = crossparams(c, l);
        if (r.size() == 2) {
            ts.emplace_back(r[0], +1);
            ts.emplace_back(r[1], -1);
        }
    }
    sort(ts.begin(), ts.end());
    int c = 0;
    fltype s = 0;
    for (int i = 0; i + 1 < (int)ts.size(); ++i) {
        c += ts[i].second;
        if (c <= 0) {
            s += max(min(ts[i + 1].first, (fltype)1) - max(ts[i].first, (fltype)0), (fltype)(0));
        }
    }
    return s < eps;
}


int main() {
    cin.tie(0);
    ios::sync_with_stdio(0);
    cout << fixed << setprecision(16);
    while (true) {
        int n;
        cin >> n;
        if (n == 0) {
            return 0;
        }
        vector<Circle> cs(n);
        for (int i = 0; i < n; ++i) {
            cin >> cs[i].first.first >> cs[i].first.second >> cs[i].second;
        }
        vector<Point> ps;
        ps.emplace_back((fltype)cs.front().first.first, (fltype)cs.front().first.second);
        ps.emplace_back((fltype)cs.back().first.first, (fltype)cs.back().first.second);
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                for (auto& p : crosspoints(cs[i], cs[j])) {
                    ps.push_back(p);
                }
            }
        }
        int m = ps.size();
        vector<fltype> dist(m, inf * 2);
        vector<bool> used(m);
        dist[0] = 0;
        while (true) {
            int nex = -1;
            for (int i = 0; i < m; ++i) {
                if (!used[i] && (nex == -1 || dist[i] < dist[nex])) {
                    nex = i;
                }
            }
            if (nex == -1 || dist[nex] > inf) {
                break;
            }
            used[nex] = true;
            for (int i = 0; i < m; ++i) {
                if (nex != i && through(cs, { ps[nex], ps[i] })) {
                    dist[i] = min(dist[i], dist[nex] + abs(ps[i] - ps[nex]));
                }
            }
        }
        if (dist[1] > inf) {
            cout << "impossible\n";
        }
        else {
            cout << dist[1] << '\n';
        }
    }
    return 0;
}
accuracy: 1
time_ms: 680
memory_kb: 3556
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define rrep(i, a, n) for(int i = a; i >= n; i--)
#define inr(l, x, r) (l <= x && x < r)
#define ll long long
#define ld long double

constexpr int IINF = 1001001;
constexpr ll INF = 9e18;

template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}

/*
前提
- 点(位置ベクトル)を複素数型で扱う
- 実軸(real)をx軸、虚軸(imag)をy軸として見る
- 比較するときは、計算誤差を意識して EPS で判定 (equal関数)
*/

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, n) for(int i = a; i < n; i++)

/*
前提
- 点(位置ベクトル)を複素数型で扱う
- 実軸(real)をx軸、虚軸(imag)をy軸として見る
- 比較するときは、計算誤差を意識して EPS で判定 (equal関数)
*/

namespace geometry {
    using D = long double;
    using Point = std::complex<D>;
    const D EPS = 1e-8;
    const D PI = M_PI;

    // 入出力ストリーム
    istream &operator>>(istream &is, Point &p) {
        D a, b;
        is >> a >> b;
        p = Point(a, b);
        return is;
    }
    ostream &operator<<(ostream &os, Point &p) {
        return os << fixed << setprecision(20) << p.real() << " " << p.imag();
    }

    // d 倍する
    Point operator*(Point p, D d) {
    return Point(p.real() * d, p.imag() * d);
    }
    // 等しいかどうか（誤差で判定）
    inline bool equal(D a, D b) { return fabs(a - b) < EPS; }

    // 単位ベクトル
    Point unit_vector(Point a) { return a / abs(a); };

    // 法線ベクトル (逆向きがよければ (0, -1) をかける)
    Point normal_vector(Point a, D dir=1) { return a * Point(0, dir); }

    // 内積：a・b = |a||b|cosΘ
    D dot(Point a, Point b){
        return (a.real() * b.real() + a.imag() * b.imag());
    }

    // 外積：a×b = |a||b|sinΘ (外積の大きさではないか？)
    D cross(Point a, Point b){
        return (a.real() * b.imag() - a.imag() * b.real());
    }

    // 反時計回りに theta 回転
    Point rotate(Point a, D theta) {
        D c = cos(theta), s = sin(theta);
        return Point(c * a.real() - s * a.imag(), s * a.real() + c * a.imag());
    }

    // 直線 
    struct Line {
        Point a, b;
        Line() = default;
        Line(Point a_, Point b_) : a(a_), b(b_) { assert(a_ != b_); };
        // Ax+By=C
        Line(D A, D B, D C){
            if(equal(A, 0)){
                a = Point(0, C/B), b = Point(1, C/B);
            }else if(equal(B, 0)){
                b = Point(C/A, 0), a = Point(C/A, 1);
            }else{
                a = Point(0, C/B), b = Point(C/A, 0);
            }
        }
    };

    // 線分（Line と同じ）
    struct Segment : Line {
        Segment() = default;
        Segment(Point a_, Point b_) : Line(a_, b_) {};
    };

    // 円（中心と半径）
    struct Circle {
        Point p;
        D r;
        Circle() = default;
        Circle(Point p_, D r_) : p(p_), r(r_) {}
    };

    // 射影：直線（線分）に 点p から引いた垂線の足を求める
    Point projection(Line l, Point p){
        D t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
        return l.a + (l.a - l.b) * t;
    }
    Point projection(Segment l, Point p){
        D t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
        return l.a + (l.a - l.b) * t;
    }

    // 反射：直線を対象軸として 点p と線対称の位置にある点を求める
    Point reflection(Line l, Point p){
        return p + (projection(l, p)-p) * 2.0;
    }

    // 3点 a, b, c の位置関係
    // reference: https://sen-comp.hatenablog.com/entry/2020/03/12/145742#iSP3%E7%82%B9%E3%81%AE%E4%BD%8D%E7%BD%AE%E9%96%A2%E4%BF%82
    int ccw(Point a, Point b, Point c){
        b -= a, c -= a;
        // 点 a, b, c が
        if(cross(b, c) >  EPS) return  1; // 反時計回りのとき
        if(cross(b, c) < -EPS) return -1; // 時計回りのとき
        
        // 同一直線上にある場合
        if(dot(b, c) < 0) return 2;      // c, a, b の順
        if(norm(b) < norm(c)) return -2; // a, b, c の順
        return 0;                        // a, c, b の順
    }
    
    // 垂直（内積 == 0）
    bool is_vertical(Line a, Line b){
        return equal(dot(a.b - a.a, b.b - b.a), 0);
    }

    // 平行（外積 == 0）
    bool is_parallel(Line a, Line b){
        return equal(cross(a.b - a.a, b.b - b.a), 0);
    }

    // 線分の交差判定（線分 s に対して, 線分 t の端点が反対側にあればよい）
    bool is_intersect(Segment s, Segment t){
        return (ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0) && 
               (ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0);
    }

    // 交点（交差する前提）
    Point cross_point(Line s, Line t){
        D d1 = cross(s.b - s.a, t.b - t.a);
        D d2 = cross(s.b - s.a, s.b - t.a);
        // s, t が一致する場合（適当な１点を返す）
        if(equal(abs(d1), 0) && equal(abs(d2), 0)) return t.a; 
        
        return t.a + (t.b - t.a) * (d2/d1);
    }
    Point cross_point(Segment s, Segment t) {
        assert(is_intersect(s, t)); // 交差する前提
        return cross_point(Line(s), Line(t));
    }

    // 線分と点の距離（点p から線分のどこかへの最短距離）
    D dist_segment_point(Segment l, Point p){
        if(dot(l.b - l.a, p - l.a) < EPS) return abs(p - l.a);
        if(dot(l.a - l.b, p - l.b) < EPS) return abs(p - l.b);
        return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
    }
    // 線分と線分の距離
    D dist_segment_segment(Segment s, Segment t){
        if(is_intersect(s, t)) return 0.0;
        D res = min({
            dist_segment_point(s, t.a),
            dist_segment_point(s, t.b),
            dist_segment_point(t, s.a),
            dist_segment_point(t, s.b),
        });
        return res;
    }

    // Todo : 円, 多角形

    // ２つの円の交点
    pair< Point, Point > cross_point(const Circle &c1, const Circle &c2) {
        D d = abs(c1.p - c2.p);
        D a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
        D t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
        Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
        Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
        return {p1, p2};
    }
};
using namespace geometry;


int main(){
    while(true){
        int n; cin >> n;
        if(n == 0) break;
        vector<Circle> circle(n);
        rep(i, 0, n){
            Point c;
            D r; cin >> c >> r;
            circle[i] = Circle(c, r);
        }
        
        vector<Segment> m(n-1);
        vector<vector<Point>> p(n+1, vector<Point>(2));
        p[0][0] = p[0][1] = circle[0].p;
        rep(i, 0, n-1){
            auto [p1, p2] = cross_point(circle[i], circle[i+1]);
            p[i+1][0] = p1, p[i+1][1] = p2;
            m[i] = Segment(p1, p2);
        }
        p[n][0] = p[n][1] = circle[n-1].p;
        vector<vector<D>> dp(n+1, vector<D>(2, 9e18));
        dp[0][0] = dp[0][1] = 0.0;
        rep(r, 1, n+1){
            rep(l, 0, r){
                rep(i1, 0, 2) rep(i2, 0, 2){
                    // p[l][i1] -> p[r][i2]
                    bool ok = true;
                    Segment s = Segment(p[l][i1], p[r][i2]);
                    rep(k, l, r-1){
                        ok &= is_intersect(s, m[k]);
                    }
                    if(ok){
                        chmin(dp[r][i2], dp[l][i1]+fabs(p[l][i1]-p[r][i2])); 
                    }
                }
            }
        }
        printf("%.20Lf\n", min(dp[n][0], dp[n][1]));
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 3864
final_rank: 9

code_snippet: #include <bits/stdc++.h>

#include <algorithm>
#include <cmath>
#include <iostream>
#include <iterator>
#include <utility>
#include <vector>

using Real = long double;
constexpr Real EPS = 1e-10;
const Real PI = std::acos(-1);

int sign(Real x) {
    return x < -EPS ? -1 : x > EPS ? 1 : 0;
}

bool eq(Real lhs, Real rhs) {
    return sign(rhs - lhs) == 0;
}

bool lte(Real lhs, Real rhs) {
    return sign(rhs - lhs) >= 0;
}

bool lt(Real lhs, Real rhs) {
    return sign(rhs - lhs) > 0;
}

bool gte(Real lhs, Real rhs) {
    return lte(rhs, lhs);
}

bool gt(Real lhs, Real rhs) {
    return lt(rhs, lhs);
}

struct Point {
    Real x;
    Real y;
    Point(Real x = 0, Real y = 0): x(x), y(y) {}
    Point& operator+=(Point rhs) {
        this->x += rhs.x;
        this->y += rhs.y;
        return *this;
    }
    Point& operator-=(Point rhs) {
        this->x -= rhs.x;
        this->y -= rhs.y;
        return *this;
    }
    Point& operator*=(Real k) {
        this->x *= k;
        this->y *= k;
        return *this;
    }
    Point& operator/=(Real k) {
        this->x /= k;
        this->y /= k;
        return *this;
    }
    friend Point operator+(Point lhs, Point rhs) {
        return lhs += rhs;
    }
    friend Point operator-(Point lhs, Point rhs) {
        return lhs -= rhs;
    }
    friend Point operator*(Point p, Real k) {
        return p *= k;
    }
    friend Point operator/(Point p, Real k) {
        return p /= k;
    }
    friend Point operator*(Real k, Point p) {
        return p * k;
    }
    friend Point operator/(Real k, Point p) {
        return p / k;
    }
    friend bool operator==(Point lhs, Point rhs) {
        return eq(lhs.x, rhs.x) && eq(lhs.y, rhs.y);
    }
    friend bool operator!=(Point lhs, Point rhs) {
        return !(lhs == rhs);
    }
    friend bool operator<(Point lhs, Point rhs) {
        if (eq(lhs.x, rhs.x)) return lt(lhs.y, rhs.y);
        return lt(lhs.x, rhs.x);
    }
    friend bool operator<=(Point lhs, Point rhs) {
        return lhs == rhs || lhs < rhs;
    }
    friend bool operator>(Point lhs, Point rhs) {
        return rhs < lhs;
    }
    friend bool operator>=(Point lhs, Point rhs) {
        return rhs <= lhs;
    }
    friend std::istream& operator>>(std::istream& is, Point& p) {
        is >> p.x >> p.y;
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, Point& p) {
        os << p.x << ' ' << p.y;
        return os;
    }
};

using Points = std::vector<Point>;
using Polygon = Points;
using Vector = Point;

Real norm(Vector p) {
    return p.x * p.x + p.y * p.y;
}

Real abs(Vector p) {
    return std::sqrt(norm(p));
}

Real dot(Vector lhs, Vector rhs) {
    return lhs.x * rhs.x + lhs.y * rhs.y;
}

Real cross(Vector lhs, Vector rhs) {
    return lhs.x * rhs.y - lhs.y * rhs.x;
}

int ccw(Point p0, Point p1, Point p2) {
    Vector a = p1 - p0;
    Vector b = p2 - p0;

    if (cross(a, b) > EPS) return 1;

    if (cross(a, b) < -EPS) return -1;

    if (dot(a, b) < 0) return 2;

    if (norm(a) < norm(b)) return -2;

    return 0;
}

struct Circle {
    Real r;
    Point c;
    Circle() {}
    Circle(Real r, Point c): r(r), c(c) {}
};

struct Segment {
    Segment() {}
    Segment(Point p0, Point p1): p0(p0), p1(p1) {}
    Point p0, p1;
};

struct Line {
    Line() {}
    Line(Point p0, Point p1): p0(p0), p1(p1) {}
    explicit Line(Segment s): p0(s.p0), p1(s.p1) {}
    Point p0, p1;
};

Real distance(Point lhs, Point rhs) {
    return abs(rhs - lhs);
}

Real distance(Line l, Point p) {
    return std::abs(cross(l.p1 - l.p0, p - l.p0)) / abs(l.p1 - l.p0);
}

Real distance(Segment s, Point p) {
    if (dot(s.p1 - s.p0, p - s.p0) < 0) {
        return distance(p, s.p0);
    }
    if (dot(s.p0 - s.p1, p - s.p1) < 0) {
        return distance(p, s.p1);
    }
    return distance(Line(s), p);
}

bool intersect(Segment lhs, Segment rhs) {
    return ccw(lhs.p0, lhs.p1, rhs.p0) * ccw(lhs.p0, lhs.p1, rhs.p1) <= 0
           && ccw(rhs.p0, rhs.p1, lhs.p0) * ccw(rhs.p0, rhs.p1, lhs.p1) <= 0;
}

bool intersect(Segment s, Point p) {
    return ccw(s.p0, s.p1, p) == 0;
}

Real distance(Segment lhs, Segment rhs) {
    if (intersect(lhs, rhs)) {
        return Real(0);
    }
    return std::min({distance(lhs, rhs.p0), distance(lhs, rhs.p1), distance(rhs, lhs.p0), distance(rhs, lhs.p1)});
}

bool parallel(Vector lhs, Vector rhs) {
    return eq(cross(lhs, rhs), 0);
}

bool parallel(Segment lhs, Segment rhs) {
    return parallel(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

bool parallel(Line lhs, Line rhs) {
    return parallel(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

Point crosspoint(Line lhs, Line rhs) {
    Real a = cross(lhs.p1 - lhs.p0, rhs.p1 - rhs.p0);
    Real b = cross(lhs.p1 - lhs.p0, lhs.p1 - rhs.p0);
    return rhs.p0 + (rhs.p1 - rhs.p0) * b / a;
}

bool intersect(Line l, Segment s) {
    return ccw(l.p0, l.p1, s.p0) * ccw(l.p0, l.p1, s.p1) <= 0;
}

bool intersect(Circle c, Line l) {
    return lte(distance(l, c.c), c.r);
}

bool contain(Circle c, Point p) {
    return lt(distance(c.c, p), c.r);
}

bool intersect(Circle c, Point p) {
    return eq(distance(c.c, p), c.r);
}

bool internal(Circle c, Point p) {
    return lt(distance(c.c, p), c.r);
}

bool intersect(Circle c, Segment s) {
    return lte(distance(s, c.c), c.r);
}

int intersect(Circle lhs, Circle rhs) {
    if (gt(lhs.r, rhs.r)) std::swap(lhs, rhs);
    Real d = distance(lhs.c, rhs.c);
    if (lt(lhs.r + rhs.r, d)) return 4;
    if (eq(lhs.r + rhs.r, d)) return 3;
    if (gt(lhs.r + d, rhs.r)) return 2;
    if (eq(lhs.r + d, rhs.r)) return 1;
    return 0;
}

bool orthogonal(Vector lhs, Vector rhs) {
    return eq(dot(lhs, rhs), 0);
}

bool orthogonal(Segment lhs, Segment rhs) {
    return orthogonal(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

bool orthogonal(Line lhs, Line rhs) {
    return orthogonal(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

Point crosspoint(Segment lhs, Segment rhs) {
    Real d0 = distance(Line(lhs.p0, lhs.p1), rhs.p0);
    Real d1 = distance(Line(lhs.p0, lhs.p1), rhs.p1);
    return rhs.p0 + (rhs.p1 - rhs.p0) * (d0 / (d0 + d1));
}

Real arg(Vector p) {
    return std::atan2(p.y, p.x);
}

Vector polar(Real a, Real r) {
    return Point(std::cos(r) * a, std::sin(r) * a);
}

Point rotate(Point p, Real t) {
    Point v = polar(1, t);
    return Point(p.x * v.x - p.y * v.y, p.x * v.y + p.y * v.x);
}

Real angle(Point p0, Point p1, Point p2) {
    Real a = arg(p0 - p1);
    Real b = arg(p2 - p1);
    if (gt(a, b)) std::swap(a, b);
    return std::min(b - a, 2 * PI - (b - a));
}

Points crosspoint(Circle lhs, Circle rhs) {
    Real d = abs(lhs.c - rhs.c);
    if (eq(d, lhs.r + rhs.r)) return {lhs.c + lhs.r * (rhs.c - lhs.c) / d};
    Real a = std::acos((lhs.r * lhs.r + d * d - rhs.r * rhs.r) / (2 * lhs.r * d));
    Real t = arg(rhs.c - lhs.c);
    return {lhs.c + polar(lhs.r, t + a), lhs.c + polar(lhs.r, t - a)};
}

Point projection(Segment s, Point p) {
    Vector a = p - s.p0;
    Vector b = s.p1 - s.p0;
    Real t = dot(a, b) / norm(b);
    return s.p0 + t * b;
}

Point projection(Line l, Point p) {
    Vector a = p - l.p0;
    Vector b = l.p1 - l.p0;
    Real t = dot(a, b) / norm(b);
    return l.p0 + t * b;
}

Point reflection(Segment s, Point p) {
    return p + (projection(s, p) - p) * Real(2);
}

Point reflection(Line l, Point p) {
    return p + (projection(l, p) - p) * Real(2);
}

Points crosspoint(Circle c, Line l) {
    Vector p = projection(l, c.c);
    Real t = std::sqrt(c.r * c.r - norm(c.c - p));
    if (eq(t, 0)) return {p};
    Vector e = (l.p1 - l.p0) / abs(l.p1 - l.p0);
    return {p + t * e, p + -t * e};
}

Points crosspoint(Circle c, Segment s) {
    auto cps = crosspoint(c, Line(s));
    Points temp;
    for (auto cp: cps) {
        if (intersect(s, cp)) temp.push_back(cp);
    }
    return temp;
}

Real area(Polygon poly) {
    int n = poly.size();
    Real result = 0;
    for (int i = 0; i < n; i++) {
        result += cross(poly[i], poly[(i + 1) % n]);
    }
    return result / 2;
}

Real perimeter(Polygon poly) {
    int n = poly.size();
    Real result = 0;
    for (int i = 0; i < n; i++) {
        result += abs(poly[i] - poly[(i + 1) % n]);
    }
    return result;
}

bool convex(Polygon poly) {
    int n = poly.size();
    for (int i = 0; i < n; i++) {
        if (ccw(poly[i], poly[(i + 1) % n], poly[(i + 2) % n]) == -1) return false;
    }
    return true;
}

int contain(Polygon poly, Point p) {
    int n = poly.size();
    bool parity = false;
    for (int i = 0; i < n; i++) {
        Point a = poly[i] - p;
        Point b = poly[(i + 1) % n] - p;
        if (gt(a.y, b.y)) std::swap(a, b);
        if (lte(a.y, 0) && lt(0, b.y) && lt(cross(a, b), 0)) parity ^= true;
        if (eq(cross(a, b), 0) && lte(dot(a, b), 0)) return 1;
    }
    return (parity ? 2 : 0);
}

Polygon convex_hull(Points points) {
    std::sort(points.begin(), points.end());
    if (points.size() < 3) return points;
    int n = points.size();
    Points lower, upper;
    lower.push_back(points[n - 1]);
    lower.push_back(points[n - 2]);
    upper.push_back(points[0]);
    upper.push_back(points[1]);
    for (int i = n - 3; i >= 0; i--) {
        for (int m = lower.size(); m >= 2 && ccw(lower[m - 2], lower[m - 1], points[i]) >= 0; m--) {
            lower.pop_back();
        }
        lower.push_back(points[i]);
    }
    for (int i = 2; i < n; i++) {
        for (int m = upper.size(); m >= 2 && ccw(upper[m - 2], upper[m - 1], points[i]) >= 0; m--) {
            upper.pop_back();
        }
        upper.push_back(points[i]);
    }
    std::reverse(lower.begin(), lower.end());
    std::copy(std::next(upper.rbegin()), std::prev(upper.rend()), std::back_inserter(lower));
    return lower;
}

Polygon convex_cut(Polygon poly, Line l) {
    int n = poly.size();
    Polygon res;
    for (int i = 0; i < n; i++) {
        Point a = poly[i];
        Point b = poly[(i + 1) % n];
        if (ccw(l.p0, l.p1, a) != -1) res.push_back(a);
        if (ccw(l.p0, l.p1, a) * ccw(l.p0, l.p1, b) == -1) {
            res.push_back(crosspoint(l, Line(a, b)));
        }
    }
    return res;
}

Line bisector(Point p0, Point p1, Point p2) {
    return Line(p1, p1 + polar(1, angle(p0, p1, p2) / 2));
}

Circle incircle(Point p0, Point p1, Point p2) {
    Point c = crosspoint(bisector(p0, p1, p2), bisector(p1, p2, p0));
    Real r = std::abs(2 * area({p0, p1, p2}) / perimeter({p0, p1, p2}));
    return Circle(r, c);
}

Line perpendicular(Line l, Point p) {
    Vector v = l.p1 - l.p0;
    return Line(p, p + Vector(-v.y, v.x));
}

Line perpendicular_bisector(Segment s) {
    return perpendicular(Line(s), (s.p0 + s.p1) / 2);
}

Circle circumscribed_circle(Point p0, Point p1, Point p2) {
    Point c = crosspoint(perpendicular_bisector(Segment(p0, p1)), perpendicular_bisector(Segment(p0, p2)));
    return Circle(distance(p0, c), c);
}

Real area(Circle c) {
    return c.r * c.r * PI;
}

Points tangent(Circle c, Point p) {
    return crosspoint(c, Circle(std::sqrt(norm(c.c - p) - c.r * c.r), p));
}

bool contain(Polygon ps, Segment s) {
    if (contain(ps, s.p0) == 0 || contain(ps, s.p1) == 0) return false;
    int n = ps.size();
    Points cps;
    cps.push_back(s.p0);
    cps.push_back(s.p1);
    for (int i = 0; i < n; i++) {
        Segment t(ps[i], ps[(i + 1) % n]);
        if (parallel(s, t)) continue;
        if (!intersect(s, t)) continue;
        Point cp = crosspoint(s, t);
        if (cp != s.p0 && cp != s.p1 && cp != t.p0 && cp != t.p1) return false;
        cps.push_back(cp);
    }
    std::sort(cps.begin(), cps.end());
    int m = cps.size();
    for (int i = 0; i + 1 < m; i++) {
        if (contain(ps, (cps[i] + cps[i + 1]) / 2) == 0) return false;
    }
    return true;
}

std::pair<Points, Points> lower_and_upper_convex_hull(Polygon ps) {
    auto it = std::max_element(ps.begin(), ps.end());
    Points lower(ps.begin(), it + 1);
    Points upper(it, ps.end());
    upper.push_back(ps.front());
    std::reverse(upper.begin(), upper.end());
    return {lower, upper};
}

Real y(Line l, Real x) {
    if (eq(l.p0.x, l.p1.x)) return std::min(l.p0.y, l.p1.y);
    return x * (l.p1.y - l.p0.y) / (l.p1.x - l.p0.x);
}

Polygon common_polygon(Polygon lhs, Polygon rhs) {
    if (lhs.size() < 3 || rhs.size() < 3) return {};
    auto [ll, lu] = lower_and_upper_convex_hull(lhs);
    auto [rl, ru] = lower_and_upper_convex_hull(rhs);
    constexpr Real INF = 1e18;
    ll.push_back(Point(INF, INF));
    lu.push_back(Point(INF, INF));
    rl.push_back(Point(INF, INF));
    ru.push_back(Point(INF, INF));

    Points lower, upper;
    for (int i = 0, j = 0, k = 0, l = 0;;) {
        if (ll[i] <= lu[j] && ll[i] <= rl[k] && ll[i] <= ru[l]) {
            if (0 < k && 0 < l) {
                if (lte(y(Line(rl[k - 1], rl[k]), ll[i].x), ll[i].y)
                    && lte(ll[i].y, y(Line(ru[l - 1], ru[l]), ll[i].x))) {
                    lower.push_back(ll[i]);
                }
            }
            if (i + 2 < (int)ll.size()) {
                Segment lls(ll[i], ll[i + 1]);
                if (0 < k) {
                    Segment rls(rl[k - 1], rl[k]);
                    if (intersect(lls, rls) && !parallel(lls, rls)) {
                        lower.push_back(crosspoint(lls, rls));
                    }
                }
                if (0 < l) {
                    Segment rus(ru[l - 1], ru[l]);
                    if (intersect(lls, rus) && !parallel(lls, rus)) {
                        lower.push_back(crosspoint(lls, rus));
                        upper.push_back(crosspoint(lls, rus));
                    }
                }
            }
            i++;
            if (i + 1 == (int)ll.size() && j + 1 == (int)lu.size()) break;
        } else if (lu[j] <= ll[i] && lu[j] <= rl[k] && lu[j] <= ru[l]) {
            if (0 < k && 0 < l) {
                if (lte(y(Line(rl[k - 1], rl[k]), lu[j].x), lu[j].y)
                    && lte(lu[j].y, y(Line(ru[l - 1], ru[l]), lu[j].x))) {
                    upper.push_back(lu[j]);
                }
            }
            if (j + 2 < (int)lu.size()) {
                Segment lus(lu[j], lu[j + 1]);
                if (0 < l) {
                    Segment rus(ru[l - 1], ru[l]);
                    if (intersect(lus, rus) && !parallel(lus, rus)) {
                        upper.push_back(crosspoint(lus, rus));
                    }
                }
                if (0 < k) {
                    Segment rls(rl[k - 1], rl[k]);
                    if (intersect(lus, rls) && !parallel(lus, rls)) {
                        lower.push_back(crosspoint(lus, rls));
                        upper.push_back(crosspoint(lus, rls));
                    }
                }
            }
            j++;
            if (i + 1 == (int)ll.size() && j + 1 == (int)lu.size()) break;
        } else if (rl[k] <= ll[i] && rl[k] <= lu[j] && rl[k] <= ru[l]) {
            if (0 < i && 0 < j) {
                if (lte(y(Line(ll[i - 1], ll[i]), rl[k].x), rl[k].y)
                    && lte(rl[k].y, y(Line(lu[j - 1], lu[j]), rl[k].x))) {
                    lower.push_back(rl[k]);
                }
            }
            if (k + 2 < (int)rl.size()) {
                Segment rls(rl[k], rl[k + 1]);
                if (0 < i) {
                    Segment lls(ll[i - 1], ll[i]);
                    if (intersect(rls, lls) && !parallel(rls, lls)) {
                        lower.push_back(crosspoint(rls, lls));
                    }
                }
                if (0 < j) {
                    Segment lus(lu[j - 1], lu[j]);
                    if (intersect(rls, lus) && !parallel(rls, lus)) {
                        lower.push_back(crosspoint(rls, lus));
                        upper.push_back(crosspoint(rls, lus));
                    }
                }
            }
            k++;
            if (k + 1 == (int)rl.size() && l + 1 == (int)ru.size()) break;
        } else {
            if (0 < i && 0 < j) {
                if (lte(y(Line(ll[i - 1], ll[i]), ru[l].x), ru[l].y)
                    && lte(ru[l].y, y(Line(lu[j - 1], lu[j]), ru[l].x))) {
                    upper.push_back(ru[l]);
                }
            }
            if (l + 2 < (int)ru.size()) {
                Segment rus(ru[l], ru[l + 1]);
                if (0 < j) {
                    Segment lus(lu[j - 1], lu[j]);
                    if (intersect(rus, lus) && !parallel(rus, lus)) {
                        upper.push_back(crosspoint(rus, lus));
                    }
                }
                if (0 < i) {
                    Segment lls(ll[i - 1], ll[i]);
                    if (intersect(rus, lls) && !parallel(rus, lls)) {
                        lower.push_back(crosspoint(rus, lls));
                        upper.push_back(crosspoint(rus, lls));
                    }
                }
            }
            l++;
            if (k + 1 == (int)rl.size() && l + 1 == (int)ru.size()) break;
        }
    }
    if (lower.empty() && upper.empty()) return {};
    std::cerr << lhs.size() << ' ' << rhs.size() << ' ' << lower.size() << ' ' << upper.size() << std::endl;

    std::copy(std::next(upper.rbegin()), std::prev(upper.rend()), std::back_inserter(lower));
    return lower;
}

bool intersect(Polygon lhs, Polygon rhs) {
    return lt(0, area(common_polygon(lhs, rhs)));
}

using ll = long long;

#define rep(i, l, r) for (ll i = (l); i < (r); i++)
#define rrep(i, r, l) for (ll i = (r); i-- > (l);)
template <class T, class U>
bool chmin(T& lhs, U rhs) {
    return lhs > rhs ? (lhs = rhs, true) : false;
}
template <class T, class U>
bool chmax(T& lhs, U rhs) {
    return lhs < rhs ? (lhs = rhs, true) : false;
}
using namespace std;

int solve() {
    int n;
    cin >> n;
    if (n == 0) return 1;
    vector<Circle> cs(n);
    rep(i, 0, n) {
        cin >> cs[i].c >> cs[i].r;
    }

    Points ps;
    ps.push_back(cs[0].c);
    ps.push_back(cs[n - 1].c);
    rep(i, 0, n - 1) {
        auto temp = crosspoint(cs[i], cs[i + 1]);
        copy(temp.begin(), temp.end(), back_inserter(ps));
    }

    int m = ps.size();
    constexpr Real INF = 1e18;
    vector dist(m, vector<Real>(m, INF));
    rep(i, 0, m) dist[i][i] = 0;
    rep(i, 0, m) rep(j, i + 1, m) if (i != j) {
        Segment s(ps[i], ps[j]);
        Points cps;
        cps.push_back(ps[i]);
        cps.push_back(ps[j]);
        rep(k, 0, n) {
            if (intersect(cs[k], s)) {
                auto temp = crosspoint(cs[k], s);
                copy(temp.begin(), temp.end(), back_inserter(cps));
            }
        }
        sort(cps.begin(), cps.end());
        bool out = false;
        rep(k, 0, (int)cps.size() - 1) {
            Point mid = (cps[k] + cps[k + 1]) / 2;
            bool ok = false;
            rep(l, 0, n) {
                if (intersect(cs[l], mid) || contain(cs[l], mid)) {
                    ok = true;
                    break;
                }
            }
            if (!ok) {
                out = true;
                break;
            }
        }
        if (!out) {
            dist[i][j] = dist[j][i] = distance(ps[i], ps[j]);
        }
    }
    rep(k, 0, m) rep(i, 0, m) rep(j, 0, m) {
        chmin(dist[i][j], dist[i][k] + dist[k][j]);
    }
    cout << fixed << setprecision(10);
    cout << dist[0][1] << '\n';
    return 0;
}

int main() {
    while (!solve())
        ;
}
accuracy: 1
time_ms: 1510
memory_kb: 4412
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using vll = vector<ll>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
using vd = vector<double>;
using vvd = vector<vd>;
using vvvd = vector<vvd>;
#define all(A) A.begin(),A.end()
#define ALL(A) A.begin(),A.end()
#define rep(i, n) for (int i = 0; i < (int) (n); i++)
// template<class T>
// void chmax(T& p, T q) { if (p < q)p = q; };

//mugenさんライブラリ. 
#include<complex>
#define mod 1000000007
using ll = long long;
const int INF = 1000000000;
const ll LINF = 1001002003004005006ll;
int dx[] = { 1,0,-1,0 }, dy[] = { 0,1,0,-1 };
// ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
template<class T>bool chmax(T& a, const T& b) { if (a < b) { a = b; return true; }return false; }
template<class T>bool chmin(T& a, const T& b) { if (b < a) { a = b; return true; }return false; }

using Real = double;
const Real EPS = 1e-6;
const Real pi = acosl(-1);


// Point
using Point = complex<Real>;
istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
ostream& operator<<(ostream& os, Point& p) {
    return os << fixed << setprecision(12) << p.real() << ' ' << p.imag();
}
inline bool eq(Real a, Real b) {
    return fabs(a - b) < EPS;
}
Point operator*(const Point& p, const Real& d) {
    return Point(real(p) * d, imag(p) * d);
}

// Line
struct Line {
    Point p1, p2;
    Line() = default;
    Line(Point p1, Point p2) :p1(p1), p2(p2) {}
    //Ax + By = C
    Line(Real A, Real B, Real C) {
        if (eq(A, 0))     p1 = Point(0, C / B), p2 = Point(1, C / B);
        else if (eq(B, 0))p1 = Point(C / A, 0), p2 = Point(C / A, 1);
        else            p1 = Point(0, C / B), p2 = Point(C / A, 0);
    }
};

// Segment
struct Segment :Line {
    Segment() = default;
    Segment(Point p1, Point p2) :Line(p1, p2) {}
};
struct Circle {
    Point center;
    Real r;
    Circle() = default;
    Circle(Point center, Real r) :center(center), r(r) {}
};

Point rotate(Real theta, Point p) {
    return Point(cos(theta) * real(p) - sin(theta) * imag(p), sin(theta) * real(p) + cos(theta) * imag(p));
}
Real dot(Point a, Point b) {
    return real(a) * real(b) + imag(a) * imag(b);
}
Real cross(Point a, Point b) {
    return real(a) * imag(b) - imag(a) * real(b);
}
int ccw(Point a, Point b, Point c) {
    b -= a; c -= a;
    if (cross(b, c) > EPS)       return  1;//COUNTER CLOCKWISE
    else if (cross(b, c) < -EPS) return -1;//CLOCKWISE
    else if (dot(b, c) < 0)      return  2;//c--a--b ONLINE BACK
    else if (norm(b) < norm(c)) return -2;//a--b--c ONLINE FRONT
    else                     return  0;//a--c--b ON SEGMENT
}
//線分と線分の交差判定
bool intersect(Segment s, Segment t) {
    return ccw(s.p1, s.p2, t.p1) * ccw(s.p1, s.p2, t.p2) <= 0 && ccw(t.p1, t.p2, s.p1) * ccw(t.p1, t.p2, s.p2) <= 0;
}
//円と円の位置関係，共通接線の個数を返す
int intersect(Circle c1, Circle c2) {
    if (c1.r < c2.r) swap(c1, c2);
    Real d = abs(c1.center - c2.center);
    //2円が離れている
    if (c1.r + c2.r < d)     return 4;
    //2円が外接する
    if (eq(c1.r + c2.r, d)) return 3;
    //2円が交わる
    if (c1.r - c2.r < d)     return 2;
    //円が内接する
    if (eq(c1.r - c2.r, d)) return 1;
    //内包
    return 0;
}
Real dis(Point a, Point b) {
    return abs(a - b);
}
vector<Point> crosspoint(Circle c1, Circle c2) {
    vector<Point> ret;
    int isec = intersect(c1, c2);
    if (isec == 0 || isec == 4) return ret;
    Real d = abs(c1.center - c2.center);
    Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    Real t = atan2(c2.center.imag() - c1.center.imag(), c2.center.real() - c1.center.real());
    ret.push_back(c1.center + Point(cos(t + a) * c1.r, sin(t + a) * c1.r));
    ret.push_back(c1.center + Point(cos(t - a) * c1.r, sin(t - a) * c1.r));
    return ret;
}

void solve(ll N) {
    vector<Circle> C(N);
    vector<Point> CO(N);
    rep(i, N) {
        double x, y, r;
        
        cin >> x >> y >> r; CO[i] = { x,y };
        C[i] = Circle({ x,y }, r);
    }
    vector<vector<Point>> P(N - 1);
    rep(i, N - 1)P[i] = crosspoint(C[i], C[i + 1]);
    bool OK=1;
    rep(k,N-1){
        if (!intersect(Segment(P[k][0], P[k][1]), Segment(CO[0], CO[N-1])))OK = 0;
    }
    if(OK){
        cout<<dis(CO[0],CO[N-1])<<endl;
        return;
    }
    vvd dist(N - 1, vd(2, 1e18));
    rep(i, N - 1) {
        rep(j, 2) {
            OK = 1;
            rep(k, i) {
                if (!intersect(Segment(P[k][0], P[k][1]), Segment(CO[0], P[i][j])))OK = 0;
            }
            if (OK) {
                dist[i][j] = dis(CO[0], P[i][j]);
            }
        }
    }
    rep(i, N - 1) {
        chmin(dist[i][0], dist[i][1] + dis(P[i][0], P[i][1]));
        chmin(dist[i][1], dist[i][0] + dis(P[i][0], P[i][1]));
        rep(j, 2) {
            for (ll p = i + 1; p < N - 1; p++) {
                rep(q, 2) {
                    OK = 1;
                    for (ll k = i + 1; k < p; k++) {
                        if (!intersect(Segment(P[k][0], P[k][1]), Segment(P[p][q], P[i][j])))OK = 0;
                    }
                    if (OK)chmin(dist[p][q], dist[i][j] + dis(P[i][j], P[p][q]));
                }
            }
        }
    }
    double an = 1e18;
    rep(i, N - 1) {
        rep(j, 2) {
            OK = 1;
            for (ll k = i + 1; k < N - 1; k++) {
                if (!intersect(Segment(P[k][0], P[k][1]), Segment(CO[N - 1], P[i][j])))OK = 0;
            }
            if (OK)chmin(an, dist[i][j] + dis(P[i][j], CO[N - 1]));
        }
    }
    cout << fixed << setprecision(15) << an << endl;
}

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);


    ll N;
    while (cin >> N) {
        if (N == 0)return 0;
        solve(N);
    }

}
accuracy: 1
time_ms: 80
memory_kb: 4204
final_rank: 16

code_snippet: #include <bits/stdc++.h>

using R = long double;
using Point = std::pair<R, R>;
using Segment = std::pair<Point, Point>;
using Circle = std::pair<Point, R>;

std::ostream& operator<<(std::ostream& os, const Point& p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}

const R EPS{1e-12l};
const R PI{acosl(-1.0l)};

bool Negative(R v) {
    return v < -EPS;
}
bool Positive(R v) {
    return v > EPS;
}
bool Zero(R v) {
    return !Negative(v) and !Positive(v);
}
bool Smaller(R a, R b) {
    return Negative(a - b);
}
bool Bigger(R a, R b) {
    return Positive(a - b);
}
bool Equal(R a, R b) {
    return !Smaller(a, b) and !Bigger(a, b);
}

R Square(R v) {
    return (Zero(v) ? 0.0l : v * v);
}

Point operator+(const Point& p, const Point& q) {
    return Point{p.first + q.first, p.second + q.second};
}

Point operator-(const Point& p, const Point& q) {
    return Point{p.first - q.first, p.second - q.second};
}

Point operator*(const Point& p, R v) {
    return Point{ p.first * v, p.second * v };
}

Point operator/(const Point& p, R v) {
    assert(!Zero(v));
    return Point{ p.first / v, p.second / v };
}

R NormSquare(const Point& p) {
    return Square(p.first) + Square(p.second);
}

R Norm(const Point& p) {
    return sqrtl(NormSquare(p));
}

R DistanceSquare(const Point& p, const Point& q) {
    return NormSquare(p - q);
}

R Distance(const Point& p, const Point& q) {
    return sqrtl(DistanceSquare(p, q));
}

int NumberCommonTangent(const Circle& l, const Circle& r) {
    R dist{DistanceSquare(l.first, r.first)};
    R down{Square(std::abs(l.second - r.second))};
    if (Smaller(dist, down)) return 0;
    if (Equal(dist, down)) return 1;
    R up{Square(l.second + r.second)};
    if (Smaller(dist, up)) return 2;
    if (Equal(dist, up)) return 3;
    return 4;
}

// bool Intersect(const Circle& l, const Circle& r) {
//     int num{NumberCommonTangent(l, r)};
//     return 0 < num and num < 4;
// }

Point Normalized(const Point& p) {
    return p / Norm(p);
}

R ArcToRad(R arc) {
    return (arc * PI) / 180.0l;
}

Point RotatedByArc(const Point& p, R arc) {
    R rad{ArcToRad(arc)};
    return Point{
        p.first * cosl(rad) - p.second * sinl(rad),
            p.first * sinl(rad) + p.second * cosl(rad)
    };
}

std::vector<Point> CrossPoint(const Circle& l, const Circle& r) {
    {
        int num{NumberCommonTangent(l, r)};
        assert(num == 2);
    }
    // assert(Intersect(l, r));
    assert(Positive(l.second));
    assert(Positive(r.second));
    R d{Distance(l.first, r.first)};
    R cosine{(Square(l.second) + Square(d) - Square(r.second)) / (2.0l * l.second * d)};
    R rc{l.second * cosine};
    R rs{sqrtl(Square(l.second) - Square(rc))};
    Point lr{Normalized(r.first - l.first)};
    Point h{l.first + lr * rc};
    std::vector<Point> res;
    res.push_back(h + RotatedByArc(lr, 90.0l) * rs);
    res.push_back(h + RotatedByArc(lr, -90.0l) * rs);
    return res;
}

enum RELATION {
    ONLINE_FRONT = -2,
    CLOCKWISE,
    ON_SEGMENT,
    COUNTER_CLOCKWISE,
    ONLINE_BACK
};

R Cross(const Point& p, const Point& q) {
    return p.first * q.second - p.second * q.first;
}

R Dot(const Point& p, const Point& q) {
    return p.first * q.first + p.second * q.second;
}

RELATION Relation(const Point& p0, const Point& p1, const Point& p2) {
    Point a{p1 - p0}, b{p2 - p0};
    if (Positive(Cross(a, b))) return COUNTER_CLOCKWISE;
    if (Negative(Cross(a, b))) return CLOCKWISE;
    if (Negative(Dot(a, b))) return ONLINE_BACK;
    if (Smaller(NormSquare(a), NormSquare(b))) return ONLINE_FRONT;
    return ON_SEGMENT;
}

bool Straddle(const Segment& l, const Segment& r) {
    return Relation(l.first, l.second, r.first) * Relation(l.first, l.second, r.second) <= 0;
}

bool Intersect(const Segment& l, const Segment& r) {
    return Straddle(l, r) and Straddle(r, l);
}

R Length(const Segment& s) {
    return Distance(s.first, s.second);
}

bool solve() {
    int N;
    std::cin >> N;
    if (N == 0) return false;
    std::vector<Circle> C(N);
    for (auto& [p, r] : C) {
        std::cin >> p.first >> p.second >> r;
    }
    // 円の交点の列挙
    std::vector<std::vector<Point>> P(N - 1); 
    for (int i{} ; i < N - 1 ; i++) {
        for (const auto& p : CrossPoint(C[i], C[i + 1])) {
            P[i].push_back(p);
        }
    }
    // 円の交点を結ぶ線分の列挙
    std::vector<Segment> S(N - 1);
    for (int i{} ; i < N - 1 ; i++) {
        assert(P[i].size() == 2u);
        S[i] = Segment{P[i][0], P[i][1]};
    }
    // グラフの構築
    std::vector<std::vector<int>> id(N - 1);
    int n{};
    for (int i{} ; i < N - 1 ; i++) {
        id[i] = std::vector<int>(P[i].size());
        for (int j{} ; j < (int)P[i].size() ; j++) {
            id[i][j] = n++;
        }
    }
    std::vector g(n + 2, std::vector<R>(n + 2, (long double)1e18));
    for (int i{} ; i < n + 2 ; i++) {
        g[i][i] = 0;
    }
    for (int i{} ; i < N - 1 ; i++) {
        for (int j{} ; j < (int)P[i].size() ; j++) {
            bool ok{true};
            Segment s{C[0].first, P[i][j]};
            for (int k{} ; k < i ; k++) {
                ok &= Intersect(s, S[k]);
            }
            if (ok) {
                g[n][id[i][j]] = Length(s);
            }
        }
    }
    for (int i{} ; i < N - 1 ; i++) {
        for (int j{} ; j < (int)P[i].size() ; j++) {
            bool ok{true};
            Segment s{P[i][j], C[N - 1].first};
            for (int k{i + 1} ; k < N - 1 ; k++) {
                ok &= Intersect(s, S[k]);
            }
            if (ok) g[id[i][j]][n + 1] = Length(s);
        }
    }
    for (int i{} ; i < N - 1 ; i++) {
        for (int j{i + 1} ; j < N - 1 ; j++) {
            for (int k{} ; k < (int)P[i].size() ; k++) {
                for (int l{} ; l < (int)P[j].size() ; l++) {
                    Segment s{P[i][k], P[j][l]};
                    bool ok{true};
                    for (int m{i + 1} ; m < j ; m++) {
                        ok &= Intersect(s, S[m]);
                    }
                    if (ok) g[id[i][k]][id[j][l]] = Length(s);
                }
            }
        }
    }
    {
        bool ok{true};
        Segment s{C[0].first, C[N - 1].first};
        for (int i{} ; i < N - 1 ; i++) {
            ok &= Intersect(s, S[i]);
        }
        if (ok) g[n][n + 1] = Length(s);
    }
    // wf
    for (int k{} ; k < n + 2 ; k++) {
        for (int i{} ; i < n + 2 ; i++) {
            for (int j{} ; j < n + 2 ; j++) {
                g[i][j] = std::min(g[i][j], g[i][k] + g[k][j]);
            }
        }
    }
    std::cout << g[n][n + 1] << '\n';
    return true;
}


int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    std::cout << std::fixed << std::setprecision(5);
    while (solve()) ;
}
accuracy: 1
time_ms: 360
memory_kb: 4352
final_rank: 18

code_snippet: #include <bits/stdc++.h>
// #include <atcoder/modint>
#define rng(a) a.begin(),a.end()
#define rrng(a) a.rbegin(),a.rend()
#define INF 2000000000000000000
#define ull unsigned long long
#define ll long long
#define ld long double
#define pll pair<ll, ll>
using namespace std;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }

const ld EPS = 1e-7;

struct Point {
  ld x, y;
  Point(ld x = 0.0, ld y = 0.0) : x(x), y(y) {}

  ld cross(const Point &b) const {
    return x * b.y - y * b.x;
  }

  ld dot(const Point&b) const {
    return x * b.x + y * b.y;
  }
};
inline Point operator + (const Point &p, const Point &q) { return Point(p.x +q.x, p.y + q.y);}
inline Point operator - (const Point &p, const Point &q) { return Point(p.x -q.x, p.y - q.y);}
inline ld dot(const Point &p, const Point &q) {return p.x * q.x + p.y * q.y;}
inline ld abs(const Point &p) {return sqrt(dot(p, p));}
inline Point operator / (const Point &p, ld a) {return Point(p.x / a, p.y / a);}
inline Point operator * (const Point &p, const Point &q) {return Point(p.x * q.x - p.y * q.y, p.x * q.y + p.y * q.x);}
inline bool eq(const Point &p, const Point &q) {return abs(p - q) < EPS;}
// inline Point cross(const Point& p, const Point &q) {
//   return p.x * q.y - p.y * q.x;
// }

struct Circle: Point {
  ld r;
  Point p;
  Circle(Point p = Point(0.0, 0.0), ld r = 0.0): Point(p), r(r) {}
};

vector<Point> crosspoint(const Circle &e, const Circle &f) {
  vector<Point> res;
  ld d = abs(e - f);
  if (d < EPS) {
    return vector<Point>();
  }
  if (d > e.r + f.r + EPS) {
    return vector<Point>();
  }
  if (d < abs(e.r - f.r) - EPS) {
    return vector<Point>();
  }
  ld rcos = (d * d + e.r * e.r - f.r * f.r) / (2.0 * d), rsin;
  if (e.r - abs(rcos) < EPS) {
    rsin = 0;
  }
  else {
    rsin = sqrt(e.r * e.r - rcos * rcos);
  }
  Point dir = (f - e) / d;
  Point p1 = e + dir * Point(rcos, rsin);
  Point p2 = e + dir * Point(rcos, -rsin);
  res.push_back(p1);
  if (!eq(p1, p2)) {
    res.push_back(p2);
  }
  return res;
}

struct Line {
  Point begin, end;

  Line(const Point &b, const Point &e) {
    begin = b, end = e;
  }

  Point vec() const {
    return end - begin;
  }
};

int sgn(const ld a) {
  return (a < -EPS ? -1: (a > EPS ? +1: 0));
}

int iSP(const Point &a, const Point& b, const Point&c) {
  int flg = sgn((b - a).cross(c - a));
  if (flg == 1) {
    return +1;
  }
  else if (flg == -1) {
    return -1;
  }
  else {
    if (sgn((b - a).dot(c - b)) > 0)
      return +2;
    else if (sgn((a - b).dot(c - a)) > 0)
      return -2;
    else
      return 0;
  }
}

const Point error_val = {12121212.0, -3324134.0};
pair<bool, Point> segmentIntersection(const Line &s1, const Line& s2) {
  if (iSP(s1.begin, s1.end, s2.begin) * iSP(s1.begin, s1.end, s2.end) <= 0 && iSP(s2.begin, s2.end, s1.begin) * iSP(s2.begin, s2.end, s1.end) <= 0) {
    if (s1.vec().cross(s2.vec()) == 0)
      return pair<bool, Point>{true, error_val};
    else
      return make_pair(true, error_val);
  }
  return make_pair(false, error_val);
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  while (true) {
    
    ll N;
    cin >> N;
    if (N == 0) {
      break;
    }
    vector<Circle> circle(N);
    for (ll i = 0; i < N; ++i) {
      ll x, y, r;
      cin >> x >> y >> r;
      circle.at(i) = Circle(Point(x, y), r);
    }
    vector<Point> point;
    vector<Line> lines;
    point.push_back({circle.at(0).x, circle.at(0).y});
    for (ll i = 0; i < N - 1; ++i) {
      vector<Point> temp = crosspoint(circle.at(i), circle.at(i + 1));
      for (ll j = 0; j < 2; ++j) {
        point.push_back(temp.at(j));
      }
      lines.push_back(Line(temp.at(0), temp.at(1)));
    }
    point.push_back({circle.at(N - 1).x, circle.at(N - 1).y});
    ll M = point.size();
    vector<ld> dp(M, INF);
    dp.at(0) = 0;
    // for (ll i = 0; i < point.size(); ++i) {
    //   cout << point.at(i).x << ' ' << point.at(i).y << "\n";
    // }
    for (ll i = 0; i < M - 1; ++i) {
      ll i_index = (i + 1) / 2;
      for (ll j = i + 1; j < M; ++j) {
        ll j_index = min((ll)lines.size(), (j + 1) / 2);
        // cout << i << ' ' << i_index << ' ' << j << ' ' << j_index << "\n";
        bool ok = true;
        Line l = Line(point.at(i), point.at(j));
        for (ll k = i_index; k < j_index; ++k) {
          pair<bool, Point> p = segmentIntersection(l, lines.at(k));
          if (!p.first) {
            ok = false;
          }
        }
        if (ok) {
          chmin(dp.at(j), dp.at(i) + abs(point.at(i) - point.at(j)));
          // cout << "ok" << "\n";
        }
      }
    }
    cout << fixed << setprecision(15);
    // for (ll i = 0; i < M; ++i) {
    //   cout << dp.at(i) << " ";
    // }
    // cout << "\n";
    cout << dp.at(M - 1) << "\n";
  }
}
accuracy: 1
time_ms: 240
memory_kb: 3552
final_rank: 2

code_snippet: #include <iostream>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <complex>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#define rep(i,s,n) for(int i = int(s); i < int(n); i++)
#define rrep(i,s,n) for(int i = int(n) - 1; i >= int(s); i--)
#define all(v) v.begin(), v.end()

using ll = long long;
using ull = unsigned long long;
using ld = long double;

template<class T>
bool chmin(T &a, T b) {
    if(a <= b) return false;
    a = b;
    return true;
}

template<class T>
bool chmax(T &a, T b) {
    if(a >= b) return false;
    a = b;
    return true;
}

using namespace std;

using vec = complex<ld>;
const ld PI = acos(-1);

void ldout(int len = 20){
    cout << fixed << setprecision(len);
}

int sgn(ld a, const ld eps = 1e-7){
    return (a < -eps) ? -1 : (a > eps) ? 1 : 0;
}

ld dot(const vec &a, const vec &b){
    return (conj(a) * b).real();
}

ld cross(const vec &a, const vec &b){
    return (conj(a) * b).imag();
}

int isp(const vec &a, const vec &b, const vec &c){
    int cross_sgn = sgn(cross(b-a, c-a));
    if (cross_sgn == 0){
        if (sgn(dot(b-a, c-a)) < 0) return -2;
        if (sgn(dot(a-b, c-b)) < 0) return 2;
    }
    return cross_sgn;
}

vec rot90(const vec &a){
    return {-a.imag(), a.real()};
}

struct circle{
    vec c;
    ld r;
};

vector<vec> cross_point(const circle &c1, const circle &c2){
    vector<vec> ps;
    ld d = abs(c2.c-c1.c);
    ld x = (d*d + c1.r*c1.r - c2.r*c2.r) / (2*d);
    vec p = c1.c + (c2.c - c1.c) * x / d;
    vec v = rot90(c2.c - c1.c);
    v *= sqrtl(max(c1.r*c1.r-x*x,ld(0))) / abs(v);
    ps.emplace_back(p+v);
    ps.emplace_back(p-v);
    return ps;
}

struct segment{
    vec a, b;
};

bool intersection_segment(const segment &a, const segment &b){
    if (sgn(isp(a.a, a.b, b.a) * isp(a.a, a.b, b.b)) <= 0 &&
        sgn(isp(b.a, b.b, a.a) * isp(b.a, b.b, a.b)) <= 0){
            return true;
        }else{
            return false;
        }
}

const ld dinf = 2e9;

void solve(int n){
    vector<circle> a(n);
    rep(i,0,n){
        ld x, y, r; cin >> x >> y >> r;
        a[i] = circle({vec(x,y),r});
    }
    vector<vec> ps(2*n);
    rep(i,0,n-1){
        vector<vec> cur = cross_point(a[i],a[i+1]);
        ps[2*i] = cur[0];
        ps[2*i+1] = cur[1];
    }
    ps[2*n-2] = a[0].c;
    ps[2*n-1] = a[n-1].c;
    vector<vector<ld>> dist(2*n,vector<ld>(2*n,dinf));
    auto add = [&](int u, int v, ld d){
        chmin(dist[u][v],d);
        chmin(dist[v][u],d);
    };
    // 2n-2 -> i
    rep(i,0,n-1){
        vec p = ps[2*n-2];
        rep(j,0,2){
            vec q = ps[2*i+j];
            bool ok = true;
            for (int k = 0; k < i; k++){
                segment test({ps[2*k],ps[2*k+1]});
                if (intersection_segment(segment({p,q}),test)){
                    continue;
                }
                ok = false;
                break;
            }
            if (ok){
                add(2*n-2,2*i+j,abs(p-q));
            }
        }
    }
    // 2n-1 -> i
    rep(i,0,n-1){
        vec p = ps[2*n-1];
        rep(j,0,2){
            vec q = ps[2*i+j];
            bool ok = true;
            for (int k = i+1; k < n-1; k++){
                segment test({ps[2*k],ps[2*k+1]});
                if (intersection_segment(segment({p,q}),test)){
                    continue;
                }
                ok = false;
                break;
            }
            if (ok){
                add(2*n-1,2*i+j,abs(p-q));
            }
        }
    }
    // 2n-2 -> 2n-1
    {
        bool ok = true;
        vec p = ps[2*n-2], q = ps[2*n-1];
        rep(k,0,n-1){
            segment test({ps[2*k],ps[2*k+1]});
            if (intersection_segment(segment({p,q}),test)){
                continue;
            }
            ok = false;
            break;
        }
        if (ok) add(2*n-2,2*n-1,abs(p-q));
    }
    // i -> j
    rep(i1,0,n-1) rep(j1,0,2) rep(i2,i1,n-1) rep(j2,0,2){
        vec p = ps[2*i1+j1], q = ps[2*i2+j2];
        bool ok = true;
        for (int k = i1+1; k < i2; k++){
            segment test({ps[2*k],ps[2*k+1]});
            if (intersection_segment(segment({p,q}),test)){
                continue;
            }
            ok = false;
            break;
        }
        if (ok) add(2*i1+j1,2*i2+j2,abs(p-q));
    }
    rep(i,0,2*n) dist[i][i] = 0;
    rep(k,0,2*n) rep(i,0,2*n) rep(j,0,2*n){
        chmin(dist[i][j],dist[i][k]+dist[k][j]);
    }
    cout << dist[2*n-2][2*n-1] << endl;
}

int main() {
    ldout(15);
    while (true){
        int n; cin >> n;
        if (n == 0) break;
        solve(n);
    } 
}
accuracy: 1
time_ms: 220
memory_kb: 4104
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long double D;
typedef complex<D> P;
const D eps = 1e-9;
const D INF = 1000000000.0;
#define X real()
#define Y imag()
#define EQ(n,m) (abs((n) - (m)) < eps)

// 内積　dot(a,b) = |a||b|cosθ
D dot(P a, P b) {
  return (conj(a)*b).X;
}
// 外積　cross(a,b) = |a||b|sinθ
D cross(P a, P b) {
  return (conj(a)*b).Y;
}
// 点の進行方向
int ccw(P a, P b, P 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(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1)*ccw(a1, a2, b2) <= 0 &&
         ccw(b1, b2, a1)*ccw(b1, b2, a2) <= 0;
}

vector<P> crosspointCC(P a, D ar, P b, D br){
    vector<P> ps;
    P ab = b - a;
    D d = abs(ab);
    D crL = (norm(ab) + ar*ar - br*br) / (2*d);
    if (EQ(d,0) || ar < abs(crL)) return ps; // 交わりなし
    P abN = ab * P(0, sqrt(ar * ar - crL * crL) / d);
    P cp = a + crL / d * ab;
    ps.push_back(cp + abN);
    if(!EQ(norm(abN), 0)) ps.push_back(cp - abN);
    return ps;
}

int main(){
    while(true){
        int n;
        cin >> n;
        if(n == 0)break;
        vector<P> z(n);
        vector<D> r(n);
        for(int i = 0; i < n; i++){
            D x,y;
            cin >> x >> y >> r[i];
            z[i] = P(x,y);
        }
        
        vector<vector<P>> CC(n+1);
        CC[0] = vector<P> (2,z[0]);
        for(int i = 0; i < n - 1; i++){
            CC[i+1] = crosspointCC(z[i], r[i], z[i+1], r[i+1]);
        }
        CC[n] = vector<P> (2,z[n-1]);
        vector<vector<D>> dp(n+1, vector<D>(2, INF));
        dp[0][0] = 0;
        dp[0][1] = 0;
        
        for(int i = 0; i <= n; i++){
             for(int pi = 0; pi < 2; pi++){
                for(int j = i + 1; j <= n; j++){
                    for(int pj = 0; pj < 2; pj++){
                        bool ok = true;
                        for(int k = i + 1; k < j; k++){
                            ok &= isecSS(CC[i][pi], CC[j][pj], CC[k][0], CC[k][1]);
                        }
                        if(ok){
                            dp[j][pj] = min(dp[j][pj], dp[i][pi] + abs(CC[j][pj] - CC[i][pi]));
                        }
                    }
                }
            }
        }
        cout << fixed << setprecision(10) << dp[n][0] << endl;
    }
}
accuracy: 1
time_ms: 450
memory_kb: 3584
final_rank: 6

code_snippet: //#define _GLIBCXX_DEBUG

#include<bits/stdc++.h>
using namespace std;

#define endl '\n'
#define lfs cout<<fixed<<setprecision(10)
#define ALL(a)  (a).begin(),(a).end()
#define ALLR(a)  (a).rbegin(),(a).rend()
#define UNIQUE(a) (a).erase(unique((a).begin(),(a).end()),(a).end())
#define spa << " " <<
#define fi first
#define se second
#define MP make_pair
#define MT make_tuple
#define PB push_back
#define EB emplace_back
#define rep(i,n,m) for(ll i = (n); i < (ll)(m); i++)
#define rrep(i,n,m) for(ll i = (ll)(m) - 1; i >= (ll)(n); i--)
using ll = long long;
using ld = long double;
const ll MOD1 = 1e9+7;
const ll MOD9 = 998244353;
const ll INF = 1e18;
using P = pair<ll, ll>;
template<typename T> using PQ = priority_queue<T>;
template<typename T> using QP = priority_queue<T,vector<T>,greater<T>>;
template<typename T1, typename T2>bool chmin(T1 &a,T2 b){if(a>b){a=b;return true;}else return false;}
template<typename T1, typename T2>bool chmax(T1 &a,T2 b){if(a<b){a=b;return true;}else return false;}
ll median(ll a,ll b, ll c){return a+b+c-max({a,b,c})-min({a,b,c});}
void ans1(bool x){if(x) cout<<"Yes"<<endl;else cout<<"No"<<endl;}
void ans2(bool x){if(x) cout<<"YES"<<endl;else cout<<"NO"<<endl;}
void ans3(bool x){if(x) cout<<"Yay!"<<endl;else cout<<":("<<endl;}
template<typename T1,typename T2>void ans(bool x,T1 y,T2 z){if(x)cout<<y<<endl;else cout<<z<<endl;}
template<typename T1,typename T2,typename T3>void anss(T1 x,T2 y,T3 z){ans(x!=y,x,z);};
template<typename T>void debug(const T &v,ll h,ll w,string sv=" "){for(ll i=0;i<h;i++){cout<<v[i][0];for(ll j=1;j<w;j++)cout<<sv<<v[i][j];cout<<endl;}};
template<typename T>void debug(const T &v,ll n,string sv=" "){if(n!=0)cout<<v[0];for(ll i=1;i<n;i++)cout<<sv<<v[i];cout<<endl;};
template<typename T>void debug(const vector<T>&v){debug(v,v.size());}
template<typename T>void debug(const vector<vector<T>>&v){for(auto &vv:v)debug(vv,vv.size());}
template<typename T>void debug(stack<T> st){while(!st.empty()){cout<<st.top()<<" ";st.pop();}cout<<endl;}
template<typename T>void debug(queue<T> st){while(!st.empty()){cout<<st.front()<<" ";st.pop();}cout<<endl;}
template<typename T>void debug(deque<T> st){while(!st.empty()){cout<<st.front()<<" ";st.pop_front();}cout<<endl;}
template<typename T>void debug(PQ<T> st){while(!st.empty()){cout<<st.top()<<" ";st.pop();}cout<<endl;}
template<typename T>void debug(QP<T> st){while(!st.empty()){cout<<st.top()<<" ";st.pop();}cout<<endl;}
template<typename T>void debug(const set<T>&v){for(auto z:v)cout<<z<<" ";cout<<endl;}
template<typename T>void debug(const multiset<T>&v){for(auto z:v)cout<<z<<" ";cout<<endl;}
template<typename T,size_t size>void debug(const array<T, size> &a){for(auto z:a)cout<<z<<" ";cout<<endl;}
template<typename T,typename V>void debug(const map<T,V>&v){for(auto z:v)cout<<"["<<z.first<<"]="<<z.second<<",";cout<<endl;}
template<typename T>vector<vector<T>>vec(ll x, ll y, T w){vector<vector<T>>v(x,vector<T>(y,w));return v;}
ll gcd(ll x,ll y){ll r;while(y!=0&&(r=x%y)!=0){x=y;y=r;}return y==0?x:y;}
//vector<ll>dx={1,-1,0,0,1,1,-1,-1};vector<ll>dy={0,0,1,-1,1,-1,1,-1};
template<typename T>vector<T> make_v(size_t a,T b){return vector<T>(a,b);}
template<typename... Ts>auto make_v(size_t a,Ts... ts){return vector<decltype(make_v(ts...))>(a,make_v(ts...));}
template<typename T1, typename T2>ostream &operator<<(ostream &os, const pair<T1, T2>&p){return os << p.first << " " << p.second;}
template<typename T>ostream &operator<<(ostream &os, const vector<T> &v){for(auto &z:v)os << z << " ";cout<<"|"; return os;}
template<typename T>void rearrange(vector<int>&ord, vector<T>&v){
  auto tmp = v;
  for(int i=0;i<tmp.size();i++)v[i] = tmp[ord[i]];
}
template<typename Head, typename... Tail>void rearrange(vector<int>&ord,Head&& head, Tail&&... tail){
  rearrange(ord, head);
  rearrange(ord, tail...);
}
template<typename T> vector<int> ascend(const vector<T>&v){
  vector<int>ord(v.size());iota(ord.begin(),ord.end(),0);
  sort(ord.begin(),ord.end(),[&](int i,int j){return v[i]<v[j];});
  return ord;
}
template<typename T> vector<int> descend(const vector<T>&v){
  vector<int>ord(v.size());iota(ord.begin(),ord.end(),0);
  sort(ord.begin(),ord.end(),[&](int i,int j){return v[i]>v[j];});
  return ord;
}
template<typename T> vector<T> inv_perm(const vector<T>&ord){
  vector<T>inv(ord.size());
  for(int i=0;i<ord.size();i++)inv[ord[i]] = i;
  return inv;
}
ll FLOOR(ll n,ll div){assert(div>0);return n>=0?n/div:(n-div+1)/div;}
ll CEIL(ll n,ll div){assert(div>0);return n>=0?(n+div-1)/div:n/div;}
ll digitsum(ll n){ll ret=0;while(n){ret+=n%10;n/=10;}return ret;}
ll modulo(ll n,ll d){return (n%d+d)%d;};
template<typename T>T min(const vector<T>&v){return *min_element(v.begin(),v.end());}
template<typename T>T max(const vector<T>&v){return *max_element(v.begin(),v.end());}
template<typename T>T acc(const vector<T>&v){return accumulate(v.begin(),v.end(),T(0));};
template<typename T>T reverse(const T &v){return T(v.rbegin(),v.rend());};
//mt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
int popcount(ll x){return __builtin_popcountll(x);};
int poplow(ll x){return __builtin_ctzll(x);};
int pophigh(ll x){return 63 - __builtin_clzll(x);};
template<typename T>T poll(queue<T> &q){auto ret=q.front();q.pop();return ret;};
template<typename T>T poll(priority_queue<T> &q){auto ret=q.top();q.pop();return ret;};
template<typename T>T poll(QP<T> &q){auto ret=q.top();q.pop();return ret;};
template<typename T>T poll(stack<T> &s){auto ret=s.top();s.pop();return ret;};
ll MULT(ll x,ll y){if(LLONG_MAX/x<=y)return LLONG_MAX;return x*y;}
ll POW2(ll x, ll k){ll ret=1,mul=x;while(k){if(mul==LLONG_MAX)return LLONG_MAX;if(k&1)ret=MULT(ret,mul);mul=MULT(mul,mul);k>>=1;}return ret;}
ll POW(ll x, ll k){ll ret=1;for(int i=0;i<k;i++){if(LLONG_MAX/x<=ret)return LLONG_MAX;ret*=x;}return ret;}
template< typename T = int >
struct edge {
  int to;
  T cost;
  int id;
  edge():id(-1){};
  edge(int to, T cost = 1, int id = -1):to(to), cost(cost), id(id){}
  operator int() const { return to; }
};

template<typename T>
using Graph = vector<vector<edge<T>>>;
template<typename T>
Graph<T>revgraph(const Graph<T> &g){
  Graph<T>ret(g.size());
  for(int i=0;i<g.size();i++){
    for(auto e:g[i]){
      int to = e.to;
      e.to = i;
      ret[to].push_back(e);
    }
  }
  return ret;
}
template<typename T>
Graph<T> readGraph(int n,int m,int indexed=1,bool directed=false,bool weighted=false){
  Graph<T> ret(n);
  for(int es = 0; es < m; es++){
    int u,v;
    T w=1;
    cin>>u>>v;u-=indexed,v-=indexed;
    if(weighted)cin>>w;
    ret[u].emplace_back(v,w,es);
    if(!directed)ret[v].emplace_back(u,w,es);
  }
  return ret;
}
template<typename T>
Graph<T> readParent(int n,int indexed=1,bool directed=true){
  Graph<T>ret(n);
  for(int i=1;i<n;i++){
    int p;cin>>p;
    p-=indexed;
    ret[p].emplace_back(i);
    if(!directed)ret[i].emplace_back(p);
  }
  return ret;
}

template <typename F>
class
#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard)
  [[nodiscard]]
#endif  // defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard)
FixPoint final : private F
{
public:
  template <typename G>
  explicit constexpr FixPoint(G&& g) noexcept
    : F{std::forward<G>(g)}
  {}

  template <typename... Args>
  constexpr decltype(auto)
  operator()(Args&&... args) const
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ >= 9
  noexcept(noexcept(F::operator()(std::declval<FixPoint>(), std::declval<Args>()...)))
#endif  // !defined(__GNUC__) || defined(__clang__) || __GNUC__ >= 9
  {
    return F::operator()(*this, std::forward<Args>(args)...);
  }
};  // class FixPoint


#if defined(__cpp_deduction_guides)
template <typename F>
FixPoint(F&&)
-> FixPoint<std::decay_t<F>>;
#endif  // defined(__cpp_deduction_guides)


namespace
{
  template <typename F>
#if !defined(__has_cpp_attribute) || !__has_cpp_attribute(nodiscard)
  #  if defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 4)
  __attribute__((warn_unused_result))
#  elif defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_Check_return_)
  _Check_return_
#  endif  // defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 4)
#endif  // !defined(__has_cpp_attribute) || !__has_cpp_attribute(nodiscard)
  inline constexpr decltype(auto)
  makeFixPoint(F&& f) noexcept
  {
    return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
  }
}  // namespace

using Real = long double;
using Point = complex<Real>;
const Real EPS = 1e-10, PI = acos((Real)(-1.0));

inline bool eq(Real a, Real b) { return fabs(b - a) < EPS; }
inline bool eq(Point a, Point b) { return fabs(b - a) < EPS; }

Point operator*(const Point &p, const Real &d) {
  return Point(real(p) * d, imag(p) * d);
}

istream &operator>>(istream &is, Point &p) {
  Real a, b;
  is >> a >> b;
  p = Point(a, b);
  return is;
}

ostream &operator<<(ostream &os, Point &p) {
  return os << fixed << setprecision(10) << p.real() << " " << p.imag();
}

Point rotate(Real theta, const Point &p) {
  return Point(cos(theta) * p.real() - sin(theta) * p.imag(), sin(theta) * p.real() + cos(theta) * p.imag());
}

namespace std {
  bool operator<(const Point& a, const Point& b) {
    return !eq(a.real(), b.real()) ? a.real() < b.real() : a.imag() < b.imag();
  }
}

struct Line {
  Point a, b;
  Line() = default;
  Line(Point a, Point b): a(a), b(b) {}

  friend ostream &operator<<(ostream &os, Line &p) {
    return os << p.a << " to " << p.b;
  }

  friend istream &operator>>(istream &is, Line &a) {
    return is >> a.a >> a.b;
  }
};

struct Circle {
  Point c;
  Real r;
  Circle() = default;
  Circle(Point c, Real r): c(c), r(r) {}

  friend ostream &operator<<(ostream &os, Circle &c) {
    return os << c.c << ": " << c.r;
  }

  friend istream &operator>>(istream &is, Circle &c) {
    return is >> c.c >> c.r;
  }
};

Real cross(const Point &a, const Point &b) {
  return real(a) * imag(b) - real(b) * imag(a);
}

Real dot(const Point &a, const Point &b) {
  return real(a) * real(b) + imag(a) * imag(b);
}

struct Segment : Line {
  Segment() = default;
  Segment(Point a, Point b) : Line(a, b) {}
};

int ccw(const Point &a, Point b, Point c) {
  b = b - a, c = c - a;
  if (cross(b, c) > EPS) return +1; // COUNTER_CLOCKWISE
  if (cross(b, c) < -EPS) return -1; // CLOCKWISE
  if (dot(b, c) < 0) return +2; // ONLINE_BACK
  if (norm(b) < norm(c)) return -2; // ONLINE_FRONT
  return 0; // ON_SEGMENT
}

bool intersect(const Segment &s, const Point &p) {
  return ccw(s.a, s.b, p) == 0;
}

bool intersect(const Segment &s, const Segment&t) {
  return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 && ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

bool intersect(const Line &l, const Segment& s) {
  return cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < EPS;
}

int intersect(const Circle &c, const Circle &d) {
  Real dis = fabs(c.c - d.c);

  if (dis > c.r + d.r + EPS) return 4;
  else if (eq(dis, c.r + d.r)) return 3;
  else if (eq(dis, abs(c.r - d.r))) return 1;
  else if (dis + min(c.r, d.r) < max(c.r, d.r) - EPS) return 0;
  else return 2;
}

bool parallel(const Line &a, const Line &b) {
  return abs(cross(a.b - a.a, b.b - b.a)) < EPS;
}

bool orthogonal(const Line &a, const Line &b) {
  return abs(dot(a.b - a.a, b.b - b.a)) < EPS;
}

Point projection(const Line& s, const Point &p) {
  double t = dot(p - s.a, s.b - s.a) / norm(s.b - s. a);
  return s.a + (s.b - s.a) * t;
}

Point projection(const Segment& l, const Point &p) {
  return projection(Line(l), p);
}

Point reflection(const Line &l, const Point &p) {
  Point r = projection(l, p);
  return p + (r - p) * 2;
}

Real distance(const Line &l, const Point &p) {
  Point r = projection(l, p);
  return fabs(p - r);
}

Real distance(const Segment&s, const Point &p) {
  Point r = projection(s, p);
  if (intersect(s, r)) return abs(r - p);
  return min(abs(s.a - p), abs(s.b - p));
}

Real distance(const Segment& a, const Segment& b) {
  if (intersect(a, b)) return 0;
  return min({
               distance(a, b.a),
               distance(a, b.b),
               distance(b, a.a),
               distance(b, a.b)
             });
}

bool contains(const Circle &c, const Point &p) {
  if (fabs(c.c - p) < c.r + EPS) return true;
  return false;
}

Point crosspoint(const Line &l, const Line &m) {
  Real A = cross(l.b - l.a, m.b - m.a);
  Real B = cross(l.b - l.a, l.b - m.a);
  if (eq(abs(A), 0.0) && eq(abs(B), 0.0)) return m.a;
  return m.a + (m.b - m.a) * B / A;
}

Point crosspoint(const Segment& l, const Segment& m) {
  return crosspoint(Line(l), Line(m));
}

Line bisector_of_angle(const Point& a, const Point& b, const Point& c) {
  Real d_ab = fabs(a - b);
  Real d_bc = fabs(b - c);
  Point c_ = b + (c - b) * d_ab / d_bc;

  return Line(b, (a + c_) * 0.5);
}

Line bisector(const Point& a, const Point& b) {
  Point c = (a + b) * 0.5;
  Point d = c + rotate(PI / 2, b - c);

  return Line(c, d);
}

int intersect(const Circle &c, const Line &l) {
  Real d = distance(l, c.c);

  if (d > c.r + EPS) return 0;
  if (eq(d, c.r)) return 1;
  return 2;
}

pair<Point, Point> crosspoint(const Circle& c, const Line& l) {
  Real d = distance(l, c.c);

  Point p = projection(l, c.c);
  if (intersect(c, l) == 1) return { p, p };

  Real d2 = sqrt(c.r * c.r - d * d);
  Point v = (l.a - l.b) * (1.0 / fabs(l.a - l.b));

  return { p + d2 * v, p - d2 * v };
}

vector<Point> crosspoint(const Circle &c, const Segment &s) {
  int isect = intersect(c, Line(s));
  if (isect == 0) return {};

  auto [p1, p2] = crosspoint(c, Line(s));

  vector<Point> res;
  if (ccw(s.a, s.b, p1) == 0) res.push_back(p1);
  if (ccw(s.a, s.b, p2) == 0) res.push_back(p2);

  if (res.size() == 2 && ccw(s.a, p1, p2) == 0) swap(res[0], res[1]);
  return res;
}

pair<Point, Point> crosspoint(const Circle &c1, const Circle &c2) {
  Real d = fabs(c1.c - c2.c);
  Real x1 = (c1.r * c1.r + d * d - c2.r * c2.r) / 2 / d;
  Real y = sqrt(c1.r * c1.r - x1 * x1);

  Point base = (c2.c * x1 + c1.c * (d - x1)) * (1.0 / d);
  Point v = rotate(PI / 2, c1.c - base);
  v = v / fabs(v);

  return { base + v * y, base - v * y };
}

pair<Point, Point> tangent(const Circle &c, const Point &p) {
  Real d = fabs(c.c - p);
  Real d2 = sqrt(d * d - c.r * c.r);
  Real a = acos(d2 / d);


  Point q = c.c - p;
  q = q / fabs(q);

//  cout << d << " " << d2 << " " << a << " " << q << endl;
  return {
    p + rotate(a, q) * d2,
    p + rotate(-a, q) * d2
  };
}

vector<Segment> common_tangent(Circle c1, Circle c2) {
  int isect = intersect(c1, c2);
  Real d = fabs(c1.c - c2.c);
  Point v = (c2.c - c1.c) / d;

  vector<Segment> res;

  if (isect == 0) {
    return {};
  } else if (isect == 1) {
    auto [p1, p2] = crosspoint(c1, Line(c1.c, c2.c));
    if (eq(fabs(p2 - c2.c), c2.r)) swap(p1, p2);

    Point v = p1 + rotate(PI/2, c2.c - p1);
    return { Segment(p1, v) };
  } else if (isect == 2) {
  } else if (isect == 3) {
    auto [p1, p2] = crosspoint(c1, c2);
    res.emplace_back(p1, rotate(PI / 2, c1.c - p1) + p1);
  } else {
    Real a = d * c1.r / (c1.r + c2.r);
    Point p = c1.c + v * a;

    Real d1 = sqrt(a * a - c1.r * c1.r), d2 = sqrt(norm(p - c2.c) - c2.r * c2.r);
    Real theta = acos(d1 / a);

//    cout << d << endl;
//    cout << a << " " << d1 << " " << d2 << " " << theta << ": " << p << endl;

    Point e = (c1.c - p) * (1.0 / fabs(c1.c - p));
    Point e1 = rotate(theta, e), e2 = rotate(-theta, e);

    res.emplace_back(p + e1 * d1, p - e1 * d2);
    res.emplace_back(p + e2 * d1, p - e2 * d2);
  }

  if (eq(c1.r, c2.r)) {
    Point e = rotate(PI / 2, v);
    res.push_back(Segment(c1.c + e * c1.r, c2.c + e * c1.r));
    res.push_back(Segment(c1.c - e * c1.r, c2.c - e * c1.r));
    return res;
  }

  if (c1.r > c2.r) swap(c1, c2);

  v = c1.c - c2.c;
  Point p = v * (1.0 / (c2.r - c1.r)) * c2.r + c2.c;

  auto [q1, q2] = tangent(c1, p);
  res.emplace_back(q1, crosspoint(c2, Line(p, q1)).first);
  res.emplace_back(q2, crosspoint(c2, Line(p, q2)).first);

  return res;
}

using Polygon = vector<Point>;

istream& operator>>(istream& is, Polygon &p) {
  for (int i = 0; i < p.size(); i++) is >> p[i];
  return is;
}

ostream& operator<<(ostream& os, Polygon &p) {
  for (int i = 0; i < p.size(); i++) os << p[i] << endl;
  return os;
}

Real area(const Polygon& p) {
  Real res = 0;
  for (int i = 0; i < p.size(); i++) {
    res += cross(p[i], p[(i + 1) % p.size()]);
  }
  return abs(res)/2;
}

Real arcarea(const Circle &c, const Point &p1_, const Point &p2_) {
  Point p1 = p1_ - c.c, p2 = p2_ - c.c;
  Real a2 = atan2(imag(p2), real(p2));
  Point p = rotate(-a2, p1);
  Real a1 = atan2(imag(p), real(p));
//  cout << p1 << " " << p2 << " --- " << a2 << " " << a1 << endl;
  return - a1 / 2 * c.r * c.r;
}

int contains(Polygon g,Point p){
  int n=g.size();
  bool x=false;
  for(int i=0;i<n;i++){
    Point a=g[i]-p,b=g[(i+1)%n]-p;
    if(fabs(cross(a,b)) < EPS and dot(a,b) < EPS) return 1;
    if(a.imag()>b.imag()) swap(a,b);
    if(a.imag() < EPS and EPS < b.imag() and cross(a,b) > EPS ) {
//      cout << a << " --- " << b << ": " << cross(a, b) << endl;
      x = !x;
    }
  }
  return (x?2:0);
}

Polygon convex_hull(Polygon Q) {
  sort(ALL(Q));

  Polygon upper({Q[0]}), lower({Q[0]});

  rep(i, 1, Q.size()) {
    while (upper.size() >= 2) {
      int _ccw = ccw(upper[upper.size() - 2], upper[upper.size() - 1], Q[i]);
      if (_ccw == 1) {
        upper.pop_back();
      } else {
        break;
      }
    }
    if (upper.size() <= 1) {
      upper.push_back(Q[i]);
    } else if (upper.size() > 1) {
      int _ccw = ccw(upper[upper.size() - 2], upper[upper.size() - 1], Q[i]);
      if (_ccw == -1 || _ccw == -2) {
        upper.push_back(Q[i]);
      }
    }
  }
  rep(i, 1, Q.size()) {
    while (lower.size() >= 2) {
      int _ccw = ccw(lower[lower.size() - 2], lower[lower.size() - 1], Q[i]);
      if (_ccw == -1) {
        lower.pop_back();
      } else {
        break;
      }
    }

    if (lower.size() <= 1) {
      lower.push_back(Q[i]);
    } else if (lower.size() > 1) {
      int _ccw = ccw(lower[lower.size() - 2], lower[lower.size() - 1], Q[i]);
      if (_ccw == 1 || _ccw == -2) {
        lower.push_back(Q[i]);
      }
    }
  }

  Polygon ret;

  rep(i, 0, lower.size()) {
    ret.push_back(lower[i]);
//    cout << lower[i] << endl;
  }

//  cout << " | " << endl;

  rrep(i, 0, upper.size()) {
    if (!eq(lower[0], upper[i]) && !eq(lower.back(), upper[i]))
      ret.push_back(upper[i]);
//    cout << upper[i] << endl;
  }

  int start = 0;

//  cout << ret.size() << endl;
  rep(i, 0, ret.size()) {
    if (ret[i].imag() < ret[start].imag() || (eq(ret[i].imag(), ret[start].imag()) && ret[i].real() < ret[start].real())) {
      start = i;
    }
  }

//  cout << ret.size() << endl;
//  rep(i, 0, ret.size()) {
//    auto p = ret[(i + start) % ret.size()];
//    cout << (int)p.real() << " " << (int)p.imag() << endl;
//  }
//
  return ret;
}

Real closest_pair(vector<Point> &pts, int l, int r) {
  int m = (l + r) / 2;
  if (r - l <= 1) {
    return INF;
  } else if (r - l == 2) {
    if (imag(pts[l]) > imag(pts[l + 1])) swap(pts[l], pts[l + 1]);
    return fabs(pts[l] - pts[l + 1]);
  }

  Real mid = pts[m].real();
  Real d = min(closest_pair(pts, l, m), closest_pair(pts, m, r));

  inplace_merge(pts.begin() + l, pts.begin() + m, pts.begin() + r, [](const Point &a, const Point& b) {
    return a.imag() < b.imag();
  });

  vector<Point> ret;

  for (int i = l; i < r; i++) {
    if (fabs(pts[i].real() - mid) >= d) continue;

    for (int j = ret.size() - 1; j >= 0; j--) {
      if (fabs(imag(ret[j]) - imag(pts[i])) >= d) break;
      chmin(d, fabs(ret[j] - pts[i]));
    }

    ret.emplace_back(pts[i]);
  }

  return d;
}

bool contains(const Polygon &a, const Polygon &b) {
  rep(i, 0, b.size()) {
    if (!contains(a, b[i])) return false;
  }
  return true;
}


Real distance(const Polygon &a, const Point &p) {
  if (contains(a, p)) return 0;
  Real res = INF;
  rep(i, 0, a.size()) {
    Segment as(a[i], a[(i + 1) % a.size()]);
    chmin(res, distance(as, p));
  }
  return res;
}

Real distance(const Polygon &a, const Polygon &b) {
//  if (contains(a, b) || contains(b, a)) return 0;
  Real res = INF;
  rep(i, 0, a.size()) chmin(res, distance(b, a[i]));
  rep(i, 0, b.size()) chmin(res, distance(a, b[i]));
  return res;
}

struct Point3d {
  Real x, y, z;
  Point3d() {}
  Point3d(Real x, Real y, Real z): x(x), y(y), z(z) {}
};

Real distance(const Point3d &a, const Point3d &b) {
  return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) + pow(a.z - b.z, 2));
}

void solve(int N) {
  vector<Circle> cs(N);
  rep(i, 0, N) cin >> cs[i];

  vector<vector<Point>> cps(N + 1);
  cps[0].push_back(cs[0].c);
  cps.back().push_back(cs.back().c);

  rep(i, 0, N - 1) {
    auto cr_ps = crosspoint(cs[i], cs[i + 1]);
    cps[i + 1].push_back(cr_ps.fi);
    cps[i + 1].push_back(cr_ps.se);
  }

  using PRI = pair<Real, int>;
  vector<vector<PRI>> G(2 * N + 2);
  int off = N + 1;
  rep(i, 0, N) {
    rep(_i, 0, cps[i].size()) {
      rep(j, i + 1, N + 1) {
        rep(_j, 0, cps[j].size()) {
          bool ok = 1;
          rep(k, i, j) {
            if (!intersect(Segment(cps[i][_i], cps[j][_j]), Segment(cps[k][0], cps[k][1]))) {
              ok = 0;
              break;
            }
          }

          if (ok) {
            G[i + _i * off].emplace_back(fabs(cps[i][_i] - cps[j][_j]), j + _j * off);
          }
        }
      }
    }
  }

  priority_queue<PRI, vector<PRI>, greater<PRI>> pq;
  vector<Real> d(G.size(), INF);
  pq.emplace(0, 0);
  d[0] = 0;
  while (pq.size()) {
    auto [dd, v] = pq.top(); pq.pop();
    if (dd > d[v]) continue;
    for (auto &[c, u] : G[v]) {
      if (c + dd < d[u]) {
        d[u] = c + dd;
        pq.emplace(d[u], u);
      }
    }
  }

  cout << d[N] << endl;
}

int main() {
  cin.tie();
  ios::sync_with_stdio(false);

  cout << fixed << setprecision(10);
  int N;
  while (cin >> N && N) {
    solve(N);
  }
}
accuracy: 1
time_ms: 70
memory_kb: 4024
final_rank: 13

code_snippet: #include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

const double EPS = 1e-8;

struct Point {
	double px, py;
};

struct Circle {
	Point e;
	double r;
};

Point operator-(const Point& a1, const Point& a2) {
	return Point{ a1.px - a2.px, a1.py - a2.py };
}

Point operator+(const Point& a1, const Point& a2) {
	return Point{ a1.px + a2.px, a1.py + a2.py };
}

double ABS(Point E) {
	return sqrt(E.px * E.px + E.py * E.py);
}

double crs(Point A1, Point A2) {
	return A1.px * A2.py - A1.py * A2.px;
}

pair<Point, Point> Cross_Circle(Circle A1, Circle A2) {
	Point Diff = A2.e - A1.e;
	double dst = ABS(Diff);
	double angle = acos((A1.r * A1.r + dst * dst - A2.r * A2.r) / (2.0 * A1.r * dst));
	double t = atan2(Diff.py, Diff.px);
	Point E1 = A1.e + Point{ A1.r * cos(angle + t), A1.r * sin(angle + t) };
	Point E2 = A1.e + Point{ A1.r * cos(-angle + t), A1.r * sin(-angle + t) };
	return make_pair(E1, E2);
}

int sgn(double E) {
	if (abs(E) <= EPS) return 0;
	else if (E > 0) return 1;
	return -1;
}

bool Intersection(Point A1, Point A2, Point A3, Point A4) {
	int V1 = sgn(crs(A2 - A1, A3 - A1));
	int V2 = sgn(crs(A2 - A1, A4 - A1));
	if (V1 == -1 && V2 == -1) return false;
	if (V1 == 1 && V2 == 1) return false;
	return true;
}

int N;
Circle C[109];

void solve() {
	// 交点の列挙
	Point ZERO = Point{ 0.0, 0.0 };
	vector<vector<Point>> Cand(N + 1, vector<Point>(2, ZERO));
	Cand[0][0] = C[1].e;
	Cand[0][1] = C[1].e;
	Cand[N][0] = C[N].e;
	Cand[N][1] = C[N].e;
	for (int i = 1; i <= N - 1; i++) {
		pair<Point, Point> ret = Cross_Circle(C[i], C[i + 1]);
		Cand[i][0] = ret.first;
		Cand[i][1] = ret.second;
	}

	// 動的計画法
	vector<vector<double>> dp(N + 1, vector<double>(2, 1e9));
	dp[0][0] = 0.0;
	dp[0][1] = 0.0;
	for (int i = 0; i < 2 * (N + 1); i++) {
		for (int j = i + 1; j < 2 * (N + 1); j++) {
			if (i == 0 && j == 6) {
				i += 0;
			}

			int L1 = i / 2, L2 = i % 2;
			int R1 = j / 2, R2 = j % 2; if (L1 == R1) continue;
			bool flag = true;
			for (int k = L1 + 1; k <= R1 - 1; k++) {
				bool ret = Intersection(Cand[L1][L2], Cand[R1][R2], Cand[k][0], Cand[k][1]);
				if (ret == false) flag = false;
			}

			// 遷移
			if (flag == true) {
				double cost = ABS(Cand[R1][R2] - Cand[L1][L2]);
				dp[R1][R2] = min(dp[R1][R2], dp[L1][L2] + cost);
			}
		}
	}

	// 出力
	printf("%.12lf\n", dp[N][0]);
}

int main() {
	while (true) {
		cin >> N; if (N == 0) break;
		for (int i = 1; i <= N; i++) cin >> C[i].e.px >> C[i].e.py >> C[i].r;
		solve();
	}
	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3968
final_rank: 12

code_snippet: #include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

struct Point {
	double px, py;
};

struct Circle {
	Point c;
	double r;
};

Point operator-(const Point& a1, const Point& a2) {
	return Point{ a1.px - a2.px, a1.py - a2.py };
}

Point operator+(const Point& a1, const Point& a2) {
	return Point{ a1.px + a2.px, a1.py + a2.py };
}

// 二点間の距離
double Dist(Point A1, Point A2) {
	double diff_x = A1.px - A2.px;
	double diff_y = A1.py - A2.py;
	return sqrt(diff_x * diff_x + diff_y * diff_y);
}

// 外積
double crs(Point A1, Point A2) {
	return A1.px * A2.py - A1.py * A2.px;
}

// 線分上で距離 dst 進んだ点
Point Walk(Point A1, Point A2, double dst) {
	Point A3 = A2 - A1;
	double vx = dst * A3.px / sqrt(A3.px * A3.px + A3.py * A3.py);
	double vy = dst * A3.py / sqrt(A3.px * A3.px + A3.py * A3.py);
	return Point{ A1.px + vx, A1.py + vy };
}

// 円と円の交点
pair<Point, Point> Cross_Circle(Circle A1, Circle A2) {
	// Step 1. 中点を求める
	double dst = Dist(A1.c, A2.c);
	double zure1 = (A1.r * A1.r - A2.r * A2.r) / (2 * dst);
	double zure2 = dst / 2.0 + zure1;
	Point Mid = Walk(A1.c, A2.c, zure2);

	// Step 2. 答えを返す
	double rem = sqrt(A1.r * A1.r - zure2 * zure2);
	Point Diff = A2.c - A1.c;
	Point Vec = Point{ -Diff.py, Diff.px };
	Point P1 = Mid + Walk(Point{ 0.0, 0.0 }, Vec, rem);
	Point P2 = Mid + Walk(Point{ 0.0, 0.0 }, Vec, -rem);
	return make_pair(P1, P2);
}

// 円と線分の距離
double Dist_Line(Point A1, Point A2, Circle R) {
	Point V1 = R.c - A1;
	Point V2 = A2 - A1;
	double val = crs(V1, V2);
	return val / Dist(A1, A2);
}

// 円と線分の交点
pair<Point, Point> Cross_Line(Point A1, Point A2, Circle R) {
	// Step 1. 中点を求める
	double dst = Dist_Line(A1, A2, R);
	double line_len = Dist(A1, A2);
	Point Diff = A2 - A1;
	Point Vec = { -Diff.py, Diff.px };
	Point Mid;
	Mid.px = R.c.px + Vec.px * (dst / line_len);
	Mid.py = R.c.py + Vec.py * (dst / line_len);

	// Step 2. 答えを返す
	double rem = sqrt(R.r * R.r - dst * dst);
	Point P1 = Mid + Walk(Point{ 0.0, 0.0 }, Diff, rem);
	Point P2 = Mid + Walk(Point{ 0.0, 0.0 }, Diff, -rem);
	return make_pair(P1, P2);
}

// A1A2 に対して A3 がどの位置にあるかを求める
double Percentage(Point A1, Point A2, Point A3) {
	if (abs(A2.px - A1.px) > abs(A2.py - A1.py)) {
		return (A3.px - A1.px) / (A2.px - A1.px);
	}
	else {
		return (A3.py - A1.py) / (A2.py - A1.py);
	}
}

int N;
Circle R[109];

bool Hantei(Point A1, Point A2) {
	// 交差する部分の区間を求める
	vector<pair<double, double>> F;
	for (int i = 1; i <= N; i++) {
		if (abs(Dist_Line(A1, A2, R[i])) >= R[i].r) continue;
		pair<Point, Point> E = Cross_Line(A1, A2, R[i]);
		double v1 = Percentage(A1, A2, E.first);
		double v2 = Percentage(A1, A2, E.second); if (v1 > v2) swap(v1, v2);
		F.push_back(make_pair(v1, v2));
	}
	sort(F.begin(), F.end());

	// 0-1 の範囲がすべて含まれているかを調べる
	double CurrentMax = 0.0;
	for (int i = 0; i < F.size(); i++) {
		if (CurrentMax + (1e-9) < F[i].first) return false;
		CurrentMax = max(CurrentMax, F[i].second);
		if (CurrentMax >= 1.0 - (1e-9)) return true;
	}
	return false;
}

void solve() {
	// あり得る点のリストを列挙する
	vector<Point> List;
	List.push_back(R[1].c);
	List.push_back(R[N].c);
	for (int i = 1; i <= N - 1; i++) {
		pair<Point, Point> E = Cross_Circle(R[i], R[i + 1]);
		List.push_back(E.first);
		List.push_back(E.second);
	}

	// 配列の初期化
	vector<vector<double>> LEN(List.size(), vector<double>(List.size(), 1e9));
	for (int i = 0; i < List.size(); i++) LEN[i][i] = 0;

	// 各線分について「通れるか」を判定する
	for (int i = 0; i < List.size(); i++) {
		for (int j = i + 1; j < List.size(); j++) {
			bool ret = Hantei(List[i], List[j]);
			if (ret == false) continue;
			LEN[i][j] = Dist(List[i], List[j]);
			LEN[j][i] = Dist(List[j], List[i]);
		}
	}

	// ベルマンフォード穂
	vector<double> dst(List.size(), 1e9);
	dst[0] = 0;
	for (int t = 0; t < List.size(); t++) {
		for (int i = 0; i < List.size(); i++) {
			for (int j = 0; j < List.size(); j++) dst[j] = min(dst[j], dst[i] + LEN[i][j]);
		}
	}

	// 出力
	printf("%.12lf\n", dst[1]);

	// デバッグ
	if (false) {
		vector<int> Path; int cx = 1;
		Path.push_back(cx);
		while (cx != 0) {
			for (int i = 0; i < List.size(); i++) {
				double goal1 = dst[i];
				double goal2 = dst[cx] - LEN[cx][i];
				if (fabs(goal1 - goal2) < 1e-6 && cx != i) { cx = i; break; }
			}
			Path.push_back(cx);
		}
		reverse(Path.begin(), Path.end());
		cout << "Path: ";
		for (int i = 0; i < Path.size(); i++) {
			if (i >= 1) cout << " -> ";
			cout << Path[i];
		}
		cout << endl;
	}
}

int main() {
	while (true) {
		// 入力
		cin >> N; if (N == 0) break;
		for (int i = 1; i <= N; i++) {
			cin >> R[i].c.px >> R[i].c.py >> R[i].r;
		}

		// 問題を解く
		solve();
	}
	return 0;
}
accuracy: 1
time_ms: 340
memory_kb: 3660
final_rank: 7

code_snippet: #pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
// #include<atcoder/modint>
using namespace std;
// using namespace atcoder;

struct Point{
	long double x;
	long double y;
};

Point operator+(const Point &l, const Point &r){
	return Point{l.x + r.x, l.y + r.y};
}

long double cross3(Point a, Point b, Point c){
	return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

bool intersect(Point a1, Point a2, Point b1, Point b2){
	bool flg1 = cross3(a1, a2, b1) * cross3(a1, a2, b2) <= 0;
	bool flg2 = cross3(b1, b2, a1) * cross3(b1, b2, a2) <= 0;
	return (flg1 && flg2);
}

long double dist(Point a, Point b){
	long double dx = a.x - b.x;
	long double dy = a.y - b.y;
	return sqrt(dx * dx + dy * dy);
}

void solve(){
	cout << fixed << setprecision(12);
	while(1){
		int n;
		cin >> n;
		if(n == 0) return;
		vector<long double> X(n), Y(n), R(n);
		for(int i = 0; i < n; i++) cin >> X[i] >> Y[i] >> R[i];

		vector<vector<Point>> LR(n + 1, vector<Point>(2));
		LR[0][0] = {X[0], Y[0]};
		LR[0][1] = {X[0], Y[0]};

		for(int i = 1; i < n; i++){
			long double d = dist(Point{X[i - 1], Y[i - 1]}, Point{X[i], Y[i]});
			long double a = acos((R[i - 1] * R[i - 1] + d * d - R[i] * R[i]) / (2.0 * R[i - 1] * d));
			long double t = atan2(Y[i] - Y[i - 1], X[i] - X[i - 1]);
			LR[i][0] = Point{X[i - 1], Y[i - 1]} + Point{cos(t + a) * R[i - 1], sin(t + a) * R[i - 1]};
			LR[i][1] = Point{X[i - 1], Y[i - 1]} + Point{cos(t - a) * R[i - 1], sin(t - a) * R[i - 1]};
		}

		LR[n][0] = {X[n - 1], Y[n - 1]};
		LR[n][1] = {X[n - 1], Y[n - 1]};

		vector<vector<long double>> dp(n + 1, vector<long double>(2, 1000000000.0));
		dp[0][0] = 0;
		dp[0][1] = 0;
		for(int i = 1; i <= n; i++){
			for(int bi = 0; bi < i; bi++){
				for(int t1 = 0; t1 < 2; t1++) for(int t2 = 0; t2 < 2; t2++){
					bool ok = true;
					for(int k = bi + 1; k < i; k++){
						if(!intersect(LR[bi][t1], LR[i][t2], LR[k][0], LR[k][1])){
							ok = false;
							break;
						}
					}
					if(ok){
						long double d = dp[bi][t1] + dist(LR[bi][t1], LR[i][t2]);
						if(d < dp[i][t2]) dp[i][t2] = d;
					}
				}
			}
		}
		cout << dp[n][0] << "\n";
	}
}

int main(){
	cin.tie(0)->sync_with_stdio(0);
	int t;
	t = 1;
	// cin >> t;
	while(t--) solve();
}
accuracy: 1
time_ms: 10
memory_kb: 3960
final_rank: 11