Official

Ex - Don't Swim Editorial by en_translator


If the line segment \(S-T\) does not cross \(C\), then the answer obviously coincides with the distance of \(S-T\). We consider the other cases.

In such case, the path can be restricted to the following:

  • Go strait from \(S\) to a vertex \(x\) of \(C\) \(\to\) walk along the circumference of \(C\) from the vertex \(x\) to another vertex \(y\) \(\to\) go strait from the vertex \(y\) to \(T\).

This is justified by the fact that any other path can be transformed to the path above which always shorten the distance.

Moreover, for the candidates of these vertices \(x\) and \(y\), it is sufficient to consider the boundary cases (that is, the line segment from \(S\) to \(x\) does not go through the interior of \(C\), but that to another vertex \(x'\), that is one of the vertices adjacent to \(x\), does; same applies to \(y\)). The validity is obvious.

There are many ways to find such vertices \(x\) and \(y\); one of the easiest ways is to find the convex hull of \(C\), \(S\), and \(T\).

After all, the path is limited to those walking along the circumference of the convex hull of \(C\), \(S\), and \(T\). Try clockwise and counterclockwise, and the smaller is the answer. The algorithm works in a total of \(O(N)\) time.

Sample code (C++):

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

using ll = long long;
using point = pair<ll, ll>;

long double dist(point a, point b) {
    return hypot<long double>(a.first - b.first, a.second - b.second);
}

ll cross(point o, point a, point b) {
    return (a.first - o.first) * (b.second - o.second) - (a.second - o.second) * (b.first - o.first);
}

vector<point> convex_hull(vector<point> ps) {
    int n = ps.size();
    sort(ps.begin(), ps.end());
    vector<point> ret;
    for(auto p : ps) {
        while((int)ret.size() >= 2 and cross(ret.end()[-1], ret.end()[-2], p) >= 0)
            ret.pop_back();
        ret.push_back(p);
    }
    int t = ret.size();
    for(int i = n - 2; i >= 0; --i) {
        auto p = ps[i];
        while((int)ret.size() > t and cross(ret.end()[-1], ret.end()[-2], p) >= 0)
            ret.pop_back();
        ret.push_back(p);
    }
    return ret;
}

long double ans, d1, d2;
int main() {
    int n;
    cin >> n;
    vector<point> c(n + 2);
    for(int i = 0; i < n + 2; i++) cin >> c[i].first >> c[i].second;
    point s = c.end()[-2], t = c.end()[-1];
    auto ch = convex_hull(c);
    int k = ch.size();
    int sid = -1, tid = -1;
    for(int i = 0; i < k; i++) {
        if(ch[i] == s) sid = i;
        if(ch[i] == t) tid = i;
    }
    if(min(sid, tid) == -1)
        ans = dist(s, t);
    else {
        for(int i = sid; i != tid; i = (i + 1) % k)
            d1 += dist(ch[i], ch[(i + 1) % k]);
        for(int i = tid; i != sid; i = (i + 1) % k)
            d2 += dist(ch[i], ch[(i + 1) % k]);
        ans = min(d1, d2);
    }
    cout << setprecision(15) << ans << endl;
}

posted:
last update: