2010-1096

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

这是一道old题,原题在[http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1185 这里]
题意是给50000个点,求最小矩形能覆盖这些点。

首先对于这50000个点求一个凸包,则可以证明矩形必有一条边和凸包上的边重合。剩下的就是旋转卡壳的经典问题了。具体旋转卡壳的资料可以在网上搜。
{{{
#!cpp
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cassert>

using namespace std;

const double EPS = 1e-8;
const int MAXN = 50005;

struct Point {
    double x, y;
    Point() {}
    Point(double x, double y) :x(x), y(y) {}
};

inline Point operator + (const Point& a, const Point& b) {
    return Point(a.x + b.x, a.y + b.y);
}

inline Point operator - (const Point& a, const Point& b) {
    return Point(a.x - b.x, a.y - b.y);
}

inline Point operator * (const Point& a, const double k) {
    return Point(a.x * k, a.y * k);
}

inline Point operator / (const Point& a, const double k) {
    return Point(a.x / k, a.y / k);
}

inline bool zero(double x) {
    return ((x > 0 ? x : -x) < EPS);
}

inline bool eq(double x, double y) {
    return x < y + EPS && y < x + EPS;
}

inline double xmult(const Point & p1, const Point & p2, const Point & p0) {
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}

inline double dmult(const Point & p1, const Point & p2, const Point & p0) {
    return (p1.x - p0.x) * (p2.x - p0.x) + (p1.y - p0.y) * (p2.y - p0.y);
}

inline double dis(const Point & p1, const Point & p2) {
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}

inline bool cmp(const Point& a, const Point& b) {
    return zero(a.y - b.y) ? (a.x > b.x + EPS) : (a.y > b.y + EPS);
}

inline int graham(int n, Point p[], Point ch[], bool maxsize = false) {
    const double e1 = maxsize ? EPS : -EPS;
    int i, j, k;
    if (n < 3) {
        for(i = 0; i < n; i++) {
            ch[i] = p[i];
        }
        return n;
    }
    sort(p, p + n, cmp);
    ch[0] = p[0];
    ch[1] = p[1];
    for (i = j = 2; i < n; ch[j++] = p[i++]) {
        while (j > 1 && xmult(ch[j - 2], p[i], ch[j - 1]) > e1) {
            j--;
        }
    }
    ch[k = j++] = p[n - 2];
    for (i = n - 3; i > 0; ch[j++] = p[i--]) {
        while (j > k && xmult(ch[j - 2], p[i], ch[j - 1]) > e1) {
            j--;
        }
    }
    while (j > k && xmult(ch[j - 2], ch[0], ch[j - 1]) > e1) {
        j--;
    }
    return j;
}

inline Point intersection(const Point & u1, const Point & u2, const Point & v1, const Point & v2) {
    Point ret = u1;
    double t = ((u1.x - v1.x) * (v1.y - v2.y) - (u1.y - v1.y) * (v1.x - v2.x)) / ((u1.x - u2.x) * (v1.y - v2.y) - (u1.y - u2.y) * (v1.x - v2.x));
    ret.x += (u2.x - u1.x) * t;
    ret.y += (u2.y - u1.y) * t;
    return ret;
}

inline Point ptoline(const Point & p, const Point & l1, const Point & l2) {
    Point t = p;
    t.x += l1.y - l2.y;
    t.y += l2.x - l1.x;
    return intersection(p, t, l1, l2);
}

inline double disptoline(const Point & p, const Point & l1, const Point & l2) {
    return fabs(xmult(p, l1, l2)) / dis(l1, l2);
}

int n;
int next[MAXN];
Point p[MAXN], ch[MAXN];

inline double times(const Point& p, const Point& a, const Point& b) {
    if (fabs(a.y - b.y) > fabs(a.x - b.x)) {
        return (p.y - a.y) / (b.y - a.y);
    } else {
        return (p.x - a.x) / (b.x - a.x);
    }
}

inline void generate(const Point& p1, const Point& p2, const Point& p3, const Point& p4, const Point& p5,
                    double& area, Point* p) {
    p[0] = ptoline(p5, p1, p2);
    p[1] = ptoline(p3, p1, p2);
    Point r = p2 - p1 + p4;
    p[2] = ptoline(p3, p4, r);
    p[3] = ptoline(p5, p4, r);
    double t = disptoline(p4, p1, p2);
    area = dis(p[0], p[1]) * t;
}

int main() {
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i < n; i++) {
            scanf("%lf%lf", &ch[i].x, &ch[i].y);
        }
        n = graham(n, ch, p);
        for (int i = 0; i < n; i++) {
            next[i] = (i + 1) % n;
        }
        int b = 1, c = 1, d = 0;
        for (int i = 2; i < n; i++) {
            Point t = ptoline(p[i], p[0], p[1]);
            if (times(t, p[0], p[1]) > times(ptoline(p[b], p[0], p[1]), p[0], p[1])) {
                b = i;
            }
            if (times(t, p[1], p[0]) > times(ptoline(p[d], p[0], p[1]), p[1], p[0])) {
                d = i;
            }
            if (disptoline(p[i], p[0], p[1]) > disptoline(p[c], p[0], p[1])) {
                c = i;
            }
        }
        double resa = 1e100;
        Point resp[4];
        for (int i = 0; i < n; i++) {
            int k = next[i];
            while (times(ptoline(p[next[b]], p[i], p[k]), p[i], p[k]) > times(ptoline(p[b], p[i], p[k]), p[i], p[k])) {
                b = next[b];
            }
            while (times(ptoline(p[next[d]], p[i], p[k]), p[k], p[i]) > times(ptoline(p[d], p[i], p[k]), p[k], p[i])) {
                d = next[d];
            }
            while (disptoline(p[next[c]], p[i], p[k]) > disptoline(p[c], p[i], p[k])) {
                c = next[c];
            }
            double area;
            Point point[4];
            generate(p[i], p[k], p[b], p[c], p[d], area, point);
            if (area < resa) {
                resa = area;
                memcpy(resp, point, sizeof(resp));
            }
        }
        printf("%.5lf\n", fabs(resa));
        while (true) {
            bool flag = true;
            for (int i = 1; i < 4 && flag; i++) {
                if (resp[0].y > resp[i].y + EPS || (eq(resp[0].y, resp[i].y) && resp[0].x > resp[i].x + EPS)) {
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
            Point t = resp[0];
            for (int i = 0; i < 3; i++) {
                resp[i] = resp[i + 1];
            }
            resp[3] = t;
        }
        for (int i = 0; i < 4; i++) {
            if (zero(resp[i].x)) {
                resp[i].x = fabs(resp[i].x);
            }
            if (zero(resp[i].y)) {
                resp[i].y = fabs(resp[i].y);
            }
        }
        for (int i = 0; i < 4; i++) {
            printf("%.5lf %.5lf\n", resp[i].x, resp[i].y);
        }
    }
    return 0;
}
}}}

这是一道old题,原题在这里

题意是给50000个点,求最小矩形能覆盖这些点。

首先对于这50000个点求一个凸包,则可以证明矩形必有一条边和凸包上的边重合。剩下的就是旋转卡壳的经典问题了。具体旋转卡壳的资料可以在网上搜。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cassert>
using namespace std;
const double EPS = 1e-8;
const int MAXN = 50005;
struct Point {
    double x, y;
    Point() {}
    Point(double x, double y) :x(x), y(y) {}
};
inline Point operator + (const Point& a, const Point& b) {
    return Point(a.x + b.x, a.y + b.y);
}
inline Point operator - (const Point& a, const Point& b) {
    return Point(a.x - b.x, a.y - b.y);
}
inline Point operator * (const Point& a, const double k) {
    return Point(a.x * k, a.y * k);
}
inline Point operator / (const Point& a, const double k) {
    return Point(a.x / k, a.y / k);
}
inline bool zero(double x) {
    return ((x > 0 ? x : -x) < EPS);
}
inline bool eq(double x, double y) {
    return x < y + EPS && y < x + EPS;
}
inline double xmult(const Point & p1, const Point & p2, const Point & p0) {
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
inline double dmult(const Point & p1, const Point & p2, const Point & p0) {
    return (p1.x - p0.x) * (p2.x - p0.x) + (p1.y - p0.y) * (p2.y - p0.y);
}
inline double dis(const Point & p1, const Point & p2) {
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
inline bool cmp(const Point& a, const Point& b) {
    return zero(a.y - b.y) ? (a.x > b.x + EPS) : (a.y > b.y + EPS);
}
inline int graham(int n, Point p[], Point ch[], bool maxsize = false) {
    const double e1 = maxsize ? EPS : -EPS;
    int i, j, k;
    if (n < 3) {
        for(i = 0; i < n; i++) {
            ch[i] = p[i];
        }
        return n;
    }
    sort(p, p + n, cmp);
    ch[0] = p[0];
    ch[1] = p[1];
    for (i = j = 2; i < n; ch[j++] = p[i++]) {
        while (j > 1 && xmult(ch[j - 2], p[i], ch[j - 1]) > e1) {
            j--;
        }
    }
    ch[k = j++] = p[n - 2];
    for (i = n - 3; i > 0; ch[j++] = p[i--]) {
        while (j > k && xmult(ch[j - 2], p[i], ch[j - 1]) > e1) {
            j--;
        }
    }
    while (j > k && xmult(ch[j - 2], ch[0], ch[j - 1]) > e1) {
        j--;
    }
    return j;
}
inline Point intersection(const Point & u1, const Point & u2, const Point & v1, const Point & v2) {
    Point ret = u1;
    double t = ((u1.x - v1.x) * (v1.y - v2.y) - (u1.y - v1.y) * (v1.x - v2.x)) / ((u1.x - u2.x) * (v1.y - v2.y) - (u1.y - u2.y) * (v1.x - v2.x));
    ret.x += (u2.x - u1.x) * t;
    ret.y += (u2.y - u1.y) * t;
    return ret;
}
inline Point ptoline(const Point & p, const Point & l1, const Point & l2) {
    Point t = p;
    t.x += l1.y - l2.y;
    t.y += l2.x - l1.x;
    return intersection(p, t, l1, l2);
}
inline double disptoline(const Point & p, const Point & l1, const Point & l2) {
    return fabs(xmult(p, l1, l2)) / dis(l1, l2);
}
int n;
int next[MAXN];
Point p[MAXN], ch[MAXN];
inline double times(const Point& p, const Point& a, const Point& b) {
    if (fabs(a.y - b.y) > fabs(a.x - b.x)) {
        return (p.y - a.y) / (b.y - a.y);
    } else {
        return (p.x - a.x) / (b.x - a.x);
    }
}
inline void generate(const Point& p1, const Point& p2, const Point& p3, const Point& p4, const Point& p5,
                    double& area, Point* p) {
    p[0] = ptoline(p5, p1, p2);
    p[1] = ptoline(p3, p1, p2);
    Point r = p2 - p1 + p4;
    p[2] = ptoline(p3, p4, r);
    p[3] = ptoline(p5, p4, r);
    double t = disptoline(p4, p1, p2);
    area = dis(p[0], p[1]) * t;
}
int main() {
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i < n; i++) {
            scanf("%lf%lf", &ch[i].x, &ch[i].y);
        }
        n = graham(n, ch, p);
        for (int i = 0; i < n; i++) {
            next[i] = (i + 1) % n;
        }
        int b = 1, c = 1, d = 0;
        for (int i = 2; i < n; i++) {
            Point t = ptoline(p[i], p[0], p[1]);
            if (times(t, p[0], p[1]) > times(ptoline(p[b], p[0], p[1]), p[0], p[1])) {
                b = i;
            }
            if (times(t, p[1], p[0]) > times(ptoline(p[d], p[0], p[1]), p[1], p[0])) {
                d = i;
            }
            if (disptoline(p[i], p[0], p[1]) > disptoline(p[c], p[0], p[1])) {
                c = i;
            }
        }
        double resa = 1e100;
        Point resp[4];
        for (int i = 0; i < n; i++) {
            int k = next[i];
            while (times(ptoline(p[next[b]], p[i], p[k]), p[i], p[k]) > times(ptoline(p[b], p[i], p[k]), p[i], p[k])) {
                b = next[b];
            }
            while (times(ptoline(p[next[d]], p[i], p[k]), p[k], p[i]) > times(ptoline(p[d], p[i], p[k]), p[k], p[i])) {
                d = next[d];
            }
            while (disptoline(p[next[c]], p[i], p[k]) > disptoline(p[c], p[i], p[k])) {
                c = next[c];
            }
            double area;
            Point point[4];
            generate(p[i], p[k], p[b], p[c], p[d], area, point);
            if (area < resa) {
                resa = area;
                memcpy(resp, point, sizeof(resp));
            }
        }
        printf("%.5lf\n", fabs(resa));
        while (true) {
            bool flag = true;
            for (int i = 1; i < 4 && flag; i++) {
                if (resp[0].y > resp[i].y + EPS || (eq(resp[0].y, resp[i].y) && resp[0].x > resp[i].x + EPS)) {
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
            Point t = resp[0];
            for (int i = 0; i < 3; i++) {
                resp[i] = resp[i + 1];
            }
            resp[3] = t;
        }
        for (int i = 0; i < 4; i++) {
            if (zero(resp[i].x)) {
                resp[i].x = fabs(resp[i].x);
            }
            if (zero(resp[i].y)) {
                resp[i].y = fabs(resp[i].y);
            }
        }
        for (int i = 0; i < 4; i++) {
            printf("%.5lf %.5lf\n", resp[i].x, resp[i].y);
        }
    }
    return 0;
}