Delaunay 三角剖分与平面欧几里得距离最小生成树

#include <cstdio>
#include <cmath>
#include <set>
#include <list>
#include <vector>
#include <algorithm>

const int MAXN = 100005;
const double EPS = 1e-9;

int dcmp(double a, double b = 0) {
    double d = a - b;
    return std::abs(d) <= EPS ? 0 : (d > 0 ? 1 : -1);
}

double sqr(double x) { return x * x; }

struct Point {
    double x, y;
    int id;

    Point(double x = 0, double y = 0, int id = -1) : x(x), y(y), id(id) {}

    bool operator<(const Point &rhs) const {
        return dcmp(x, rhs.x) == 0 ? y < rhs.y : x < rhs.x;
    }

    Point operator+(const Point &rhs) const { return Point(x + rhs.x, y + rhs.y); }
    Point operator-(const Point &rhs) const { return Point(x - rhs.x, y - rhs.y); }
    friend double dot(const Point &a, const Point &b) { return a.x * b.x + a.y * b.y; }
    friend double cross(const Point &a, const Point &b) { return a.x * b.y - a.y * b.x; }

    friend double dist(const Point &a, const Point &b) {
        return std::sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
    }
    friend double distSqr(const Point &a, const Point &b) {
        return sqr(a.x - b.x) + sqr(a.y - b.y);
    }
} P[MAXN];

namespace Delaunay {

struct Point3D {
    double x, y, z;

    Point3D(double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z) {}
    Point3D(const Point &p) : x(p.x), y(p.y), z(sqr(p.x) + sqr(p.y)) {}

    friend Point3D operator-(const Point3D &a, const Point3D &b) {
        return Point3D(a.x - b.x, a.y - b.y, a.z - b.z);
    }

    friend double dot(const Point3D &a, const Point3D &b) {
        return a.x * b.x + a.y * b.y + a.z * b.z;
    }
    friend Point3D cross(const Point3D &a, const Point3D &b) {
        return Point3D(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
    }
    friend double mix(const Point3D &a, const Point3D &b, const Point3D &c) {
        return dot(a, cross(b, c));
    }
};

struct Edge {
    int id;
    std::list<Edge>::iterator c;

    Edge(int id = 0) : id(id) {}
};

bool doesPointInCircle(const Point &a, Point b, Point c, const Point &p) {
    if (cross(b - a, c - a) < 0) std::swap(b, c);
    Point3D a3(a), b3(b), c3(c), p3(p);
    b3 = b3 - a3, c3 = c3 - a3, p3 = p3 - a3;
    return dcmp(mix(p3, b3, c3)) < 0;
}

bool hasIntersection(const Point &a, const Point &b, const Point &c, const Point &d) {
    return dcmp(cross(c - a, b - a)) * dcmp(cross(b - a, d - a)) > 0 &&
           dcmp(cross(a - c, d - c)) * dcmp(cross(d - c, b - c)) > 0;
}

std::list<Edge> head[MAXN];
Point p[MAXN];
int n, rename[MAXN];

void addEdge(int u, int v) {
    head[u].push_front(Edge(v));
    head[v].push_front(Edge(u));
    head[u].begin()->c = head[v].begin();
    head[v].begin()->c = head[u].begin();
}

void divide(int l, int r) {
    if (r - l <= 2) {
        for (int i = l; i <= r; i++) for (int j = i + 1; j <= r; j++) addEdge(i, j);
        return;
    }

    int mid = l + ((r - l) >> 1);
    divide(l, mid);
    divide(mid + 1, r);

    int nowl = l, nowr = r;
    for (bool updated = true; updated; ) {
        updated = false;
        Point ptL = p[nowl], ptR = p[nowr];
        for (auto i : head[nowl]) {
            Point t = p[i.id];
            double v = cross(ptL - ptR, t - ptR);
            if (dcmp(v) > 0 || (!dcmp(v) && distSqr(ptR, t) < distSqr(ptR, ptL))) {
                nowl = i.id;
                updated = true;
                break;
            }
        }
        if (updated) continue;
        for (auto i : head[nowr]) {
            Point t = p[i.id];
            double v = cross(ptR - ptL, t - ptL);
            if (dcmp(v) < 0 || (!dcmp(v) && distSqr(ptL, t) < distSqr(ptL, ptR))) {
                nowr = i.id;
                updated = true;
                break;
            }
        }
    }

    addEdge(nowl, nowr);
    while (true) {
        Point ptL = p[nowl], ptR = p[nowr];
        int ch = -1, side = 0;
        for (auto i : head[nowl]) {
            Point t = p[i.id];
            if (dcmp(cross(ptR - ptL, t - ptL)) > 0
                    && (ch == -1 || doesPointInCircle(ptL, ptR, p[ch], t))) {
                ch = i.id;
                side = -1;
            }
        }
        for (auto i : head[nowr]) {
            Point t = p[i.id];
            if (dcmp(cross(t - ptR, ptL - ptR)) > 0
                    && (ch == -1 || doesPointInCircle(ptL, ptR, p[ch], t))) {
                ch = i.id;
                side = 1;
            }
        }

        if (ch == -1) break;
        if (side == -1) {
            for (auto it = head[nowl].begin(); it != head[nowl].end(); ) {
                if (hasIntersection(ptL, p[it->id], ptR, p[ch])) {
                    head[it->id].erase(it->c);
                    head[nowl].erase(it++);
                } else {
                    it++;
                }
            }
            nowl = ch;
            addEdge(nowl, nowr);
        } else {
            for (auto it = head[nowr].begin(); it != head[nowr].end(); ) {
                if (hasIntersection(ptR, p[it->id], ptL, p[ch])) {
                    head[it->id].erase(it->c);
                    head[nowr].erase(it++);
                } else {
                    it++;
                }
            }
            nowr = ch;
            addEdge(nowl, nowr);
        }
    }
}

bool isParallel(const Point &a, const Point &b, const Point &c) {
    return !dcmp(cross(b - a, c - a));
}

void getEdge(std::vector<std::pair<int, int> > &ret) {
    ret.reserve(n);
    static std::set<std::pair<int, int> > vis;
    vis.clear();

    for (int i = 0; i < n; i++) for (auto j : head[i]) {
        if (j.id < i) continue;
        Point now = p[j.id];
        for (auto k : head[i]) {
            if (k.id < i) continue;
            if (isParallel(p[i], p[j.id], p[k.id])
                    && distSqr(p[k.id], p[i]) < distSqr(now, p[i]))
                now = p[k.id];
        }
        if (vis.find(std::make_pair(p[i].id, now.id)) == vis.end()) {
            vis.insert(std::make_pair(p[i].id, now.id));
            ret.push_back(std::make_pair(p[i].id, now.id));
        }
    }
}

void init(int n, Point *P) {
    std::copy(P, P + n, p);
    std::sort(p, p + n);
    for (int i = 0; i < n; i++) rename[p[i].id] = i;
    Delaunay::n = n;
    divide(0, n - 1);
}

void split(int n, Point *P, std::vector<std::pair<int, int> > &ret) {
    for (int i = 0; i < n; i++) P[i].id = i;
    init(n, P);
    getEdge(ret);
}

} // namespace Delaunay

namespace Kruskal {

struct DJS {
    int f[MAXN];

    void init(int n) { for (int i = 1; i <= n; i++) f[i] = i; }
    int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }
    void merge(int x, int y) { f[find(y)] = find(x); }
    bool test(int x, int y) { return find(x) == find(y); }
} djs;

struct Edge {
    int u, v;
    double w;

    Edge(int u, int v, double w) : u(u), v(v), w(w) {}
};
std::vector<Edge> edges;

double solve(int n) {
    std::sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b){
            return a.w < b.w;
        });
    djs.init(n);

    double ans = 0;
    int added = 0;
    for (auto e : edges) if (!djs.test(e.u, e.v)) {
        djs.merge(e.u, e.v);
        ans += e.w;
        if (++added == n - 1) break;
    }

    return ans;
}

} // namespace Kruskal

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%lf %lf", &P[i].x, &P[i].y);

    static std::vector<std::pair<int, int> > edges;
    Delaunay::split(n, P, edges);

    for (auto e : edges) Kruskal::edges.emplace_back(e.first + 1, e.second + 1, dist(P[e.first], P[e.second]));
    double ans = Kruskal::solve(n);
    printf("%lld\n", ans);

    return 0;
}

results matching ""

    No results matching ""