poj 2069 最小球覆盖几何解法

阅读量: searchstar 2019-10-23 12:25:38
Categories: Tags:

期望复杂度O(n)

原理与最小圆覆盖类似,只是要多求一个四面体的外接圆。
坑点:poj不能srand(time(NULL)),会RE

代码:

#include <cstdio>
#include <cmath>
#include <limits>
#include <algorithm>
#include <ctime>

using namespace std;

#define MAXN 111
template <typename T>
T sq(T x) {
    return x * x;
}

const double eps = 1e-12, pi = acos(-1.0);
int sgn(double x) {
    return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1);
}
struct VEC {
    double x, y, z;

    VEC operator + (const VEC& b) const {
        return VEC{x + b.x, y + b.y, z + b.z};
    }
    VEC operator - (const VEC& b) const {
        return VEC{x - b.x, y - b.y, z - b.z};
    }

    double len2() const {
        return sq(x) + sq(y) + sq(z);
    }
    double len() const {
        return sqrt(len2());
    }

    double dist2(const VEC& b) const {
        return (*this - b).len2();
    }
    double dist(const VEC& b) const {
        return (*this - b).len();
    }

    VEC operator * (double b) const {
        return VEC{x * b, y * b, z * b};
    }
    VEC trunc(double l) const {
        double ori = len();
        if (!sgn(ori)) return *this;
        return *this * (l / ori);
    }

    VEC operator / (double b) const {
        return VEC{x / b, y / b, z / b};
    }
    VEC norm() const {
        return *this / len();
    }

    VEC operator ^ (const VEC& b) const {
        return VEC{y * b.z - b.y * z, b.x * z - x * b.z, x * b.y - b.x * y};
    }
    double operator * (const VEC& b) const {
        return x * b.x + y * b.y + z * b.z;
    }

    double rad(const VEC& b) const {
        return fabs(atan2((*this ^ b).len(), *this * b));
    }
    double rad_acos(const VEC& b) const {
        double L1 = len2(), L2 = b.len2();
        return acos((L1 + L2 - (*this - b).len2()) / (2 * sqrt(L1) * sqrt(L2)));
    }

    double MixedProd(const VEC& b, const VEC& c) const {
        return *this * (b ^ c);
    }

    void input() {
        scanf("%lf%lf%lf", &x, &y, &z);
    }
};

struct LINE {
    VEC s, v;
    double dist(const VEC& p) const {
        return (v ^ (p - s)).len() / v.len();
    }
    VEC projection(const VEC& p) const {
        VEC v1 = v.norm();
        return v1 * ((p - s) * v1) + s;
    }
};

struct CIRCLE3 {
    VEC c, v;
    double r;
};

struct BALL {
    VEC c;
    double r;

    bool operator == (const BALL& b) const {
        return sgn((c - b.c).len()) == 0 && sgn(r - b.r) == 0;
    }
    //1: inside
    //2: internally tangent
    //3: Cross
    //4: externally tangent
    //5: away from
    int relation(const BALL& b) const {
        double d = c.dist(b.c);
        int s = sgn(d - fabs(b.r - r));
        if (s <= 0) return s + 2;
        return sgn(d - (b.r + r)) + 4;
    }

    //-1: inside
    //0: on
    //1: out
    int relation(const VEC& p) const {
        return sgn(p.dist(c) - r);
    }

    //-1: infinite
    int cross(CIRCLE3& ans, const BALL& b) const {
        int rel = relation(b);
        if (rel == 1 || rel == 5) return 0;
        if (*this == b) return -1;
        ans.v = (b.c - c).norm();
        //WA ???? poj 2177
        //double d2 = c.dist2(b.c);
        //double l = (sq(r) + d2 - sq(b.r)) / (2 * sqrt(d2)); //projection of r on d
        double d = c.dist(b.c);
        double l = (sq(r) + sq(d) - sq(b.r)) / (2 * d);
        ans.c = c + ans.v * l;
        ans.r = sqrt(sq(r) - sq(l));
        return 1;
    }

    void SetCircumscribed(const VEC& A, const VEC& B) {
        c = (A + B) / 2.0;
        r = (B - A).len() / 2.0;
    }

    //outer
    //Make sure that A, B, C are not collinear
    void SetCircumscribed(const VEC& A, VEC B, VEC C) {
        B = B - A;
        C = C - A;
        VEC s2di = B ^ C;
        if (s2di.len() < eps) return;
        VEC abv = s2di ^ B;
        VEC acv = C ^ s2di;
        VEC to = (abv * C.len2() + acv * B.len2()) / (2.0 * s2di.len2());
        c = A + to;
        r = to.len();
    }
    //Make sure that A, B, C, D are not in the same plane
    void SetCircumscribed(const VEC& A, const VEC& B, const VEC& C, VEC D) {
        SetCircumscribed(A, B, C);
        VEC v = ((C - A) ^ (B - A)).norm();
        D = D - c;
        double L = D * v, d2 = (v ^ D).len2();
        if (fabs(L) < eps) return;
        double k = (d2 + sq(L) - sq(r)) / (2 * L);
        c = c + v * k;
        r = sqrt(sq(k) + sq(r));
    }
};

BALL SmallestBallCover(VEC ps[], int n) {
    random_shuffle(ps, ps + n);
    BALL ball{ps[0], 0};
    for (int i1 = 1; i1 < n; ++i1) {
        if (ball.relation(ps[i1]) == 1) {
            ball.c = ps[i1];
            for (int i2 = 0; i2 < i1; ++i2) {
                if (ball.relation(ps[i2]) == 1) {
                    ball.SetCircumscribed(ps[i1], ps[i2]);
                    for (int i3 = 0; i3 < i2; ++i3) {
                        if (ball.relation(ps[i3]) == 1) {
                            ball.SetCircumscribed(ps[i1], ps[i2], ps[i3]);
                            for (int i4 = 0; i4 < i3; ++i4) {
                                if (ball.relation(ps[i4]) == 1) {
                                    ball.SetCircumscribed(ps[i1], ps[i2], ps[i3], ps[i4]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return ball;
}

int main() {
    int n;
    static VEC ps[MAXN];

    //srand(time(NULL));
    srand(19990828);

    while (~scanf("%d", &n) && n) {
        for (int i = 0; i < n; ++i) {
            ps[i].input();
        }
        BALL ans = SmallestBallCover(ps, n);
        printf("%.5f\n", ans.r);
    }

    return 0;
}