Codeforces Gym 100633E(旋转卡壳,二分)

题意:二维平面上给N个点,求一个面积最小的矩形,使得所有点都在矩形内或矩形边上。
链接:http://codeforces.com/gym/100633/problem/E

解法:显然先求凸包,在凸包上选择矩形。用旋(xuan1)转(zhuan4)卡(qia3)壳(ke2) 的方法,O(N)枚举两条不断旋转的平行线,对于每对平行线,计算出另两条矩形的平行边,使得矩形面积最小。难点在于如何计算另两条矩形的平行边,正解如下,将凸包的每条边当成一条直线,直线与x轴正方向可求一个a角,( 0a<π ),存储下来,排序。对于每对旋转的平行边,求与其垂直的直线的b角,在凸包每条直线中找到第一个不小于b角的直线,往左取10条,往右取10条,分别将取得的直线对应线段的端点存储下来,在存储下来的点中找到两个能“包括“凸包的点,接着求得对应的矩形,不断维护面积最小的矩形,输出即可。spj,任意满足条件矩形即可。

总结:此题想法容易,编码困难,适合反复咀嚼多练,以提高对旋转卡壳、计算几何中二分的熟练度,甚而能达到简化此类题目的方法目的更好。

//Hello. I'm Peter.
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const double eps = 1e-9, pi = acos(-1.0);
inline int sgn(double x){
    if(fabs(x) < eps) return 0;
    else return x > 0? 1 : -1;
}
struct Point{
    double x, y;
    Point(){};
    Point(double x1, double y1){x = x1, y = y1;}
};
typedef Point Vector;
bool cmpxy(const Point a, const Point b){if(sgn(a.x-b.x)) return a.x < b.x; else return a.y < b.y;}
bool cmpyx(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y < b.y; else return a.x < b.x;}
bool cmpYx(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y > b.y; else return a.x < b.x;}
bool cmpyX(const Point a, const Point b){if(sgn(a.y-b.y)) return a.y < b.y; else return a.x > b.x;}
Vector operator + (const Vector a, const Vector b){return Vector(a.x + b.x, a.y + b.y);}
Vector operator - (const Vector a, const Vector b){return Vector(a.x - b.x, a.y - b.y);}
double operator * (const Vector a, const Vector b){return a.x * b.x + a.y * b.y;}
double operator % (const Vector a, const Vector b){return a.x * b.y - a.y * b.x;}
Vector operator * (const Vector a, const double b){return Vector(a.x * b, a.y * b);}
Vector operator * (const double b, const Vector a){return Vector(a.x * b, a.y * b);}
Vector operator / (const Vector a, const double b){return Vector(a.x / b, a.y / b);}
bool operator == (const Point a, const Point b){return sgn(a.x - b.x)==0 && sgn(a.y - b.y)==0;}
bool operator || (const Vector a, const Vector b){return sgn(a % b)==0;}
bool operator / (const Vector a, const Vector b){return sgn(a % b)!=0;}
double Length(Vector v){return (double)sqrt((double)(v.x * v.x + v.y * v.y));}
double Dis(Point a, Point b){return Length(a - b);}
Vector Rotate(Vector v, double rad){return Vector(v.x * cos(rad) - v.y * sin(rad), v.x * sin(rad) + v.y * cos(rad));}
Vector Norv(Vector v){return Vector(v.y, -v.x);}
Vector Unitv(Vector v){return v / Length(v);}
double angle(Vector v){return atan2(v.y, v.x);}
double angle(Vector a, Vector b){
    double ans = angle(a) - angle(b);
    while(sgn(ans) < 0) ans += 2*pi; while(sgn(ans) >= 2*pi) ans -= 2*pi;
    return fmin(ans, 2*pi - ans);
}
double Area_Tri(Point p1, Point p2, Point p3){return 0.5 * fabs((p2 - p1) % (p3 - p1));}
double Area_Tri(double a, double b, double c){
    double p = (a+b+c)/2;
    return (double)sqrt((double)(p*(p-a)*(p-b)*(p-c)));
}
double Area_Polygon(Point *p, int n){
    //求任意简单多边形的无向面积,前提要将p逆时针或顺时针排序

    if(n <= 2) return 0;
    Point o = Point(0, 0);
    p[n++] = p[0];
    double ans = 0;
    for(int i = 0; i < n-1; i++){
        ans += Area_Tri(o, p[i], p[i+1]) * (sgn((p[i] - o) % (p[i+1] - o)) < 0? -1 : 1);
    }
    return fabs(ans);
}


struct Line{
    Point p; Vector v;
    Line(){};
    Line(Point p1, Vector v1){p = p1, v = v1;}
};
Point operator / (const Line a, const Line b){
    double t = ((b.p - a.p) % b.v) / (a.v % b.v);
    return a.p + a.v * t;
}
double Dis(Point p, Line l){return fabs(l.v % (p - l.p)) / Length(l.v);}
double angle(Line a, Line b){double ans = angle(a.v, b.v); return fmin(ans, pi - ans);}
double angle(Line l){
    double a = angle(Vector(1, 0), l.v);
    if(Rotate(Vector(1, 0), a) / l.v) a = pi - a;
    return a;
}


struct Seg{
    Point p1, p2;
    Seg(){};
    Seg(Point p11, Point p22){p1 = p11, p2 = p22;}
};
bool operator / (const Seg a, const Seg b){//need change
    return sgn((a.p2 - a.p1) % (b.p1 - a.p1)) * sgn((a.p2 - a.p1) % (b.p2 - a.p1)) <= 0 &&
    sgn((b.p2 - b.p1) % (a.p1 - b.p1)) * sgn((b.p2 - b.p1) % (a.p2 - b.p1)) <= 0 ;
}
bool operator / (const Line a, const Seg b){
    return sgn(a.v % (b.p1 - a.p)) * sgn(a.v % (b.p2 - a.p)) <= 0;
}
bool PointOnSeg(Point p, Seg s){
    if((s.p1 - p) / (s.p2 - p)) return false;
    else if(sgn((s.p1 - p) * (s.p2 - p)) > 0) return false;
    else return true;
}

int n, m;
#define N 80010

Point readPoi(){
    int x, y;
    x = read(), y = read();
    return Point(x, y);
}
Seg seg[N];
struct Data{
    int id;
    double rad;
}data[N];
int numdata;
bool cmp1(const Data a, const Data b){
    return a.rad < b.rad;
}
int binary_find(double rad){
    int l = 1, r = numdata, mid;
    while(l < r){
        mid = (l + r) >> 1;
        if(sgn(data[mid].rad - rad) >= 0) r = mid;
        else l = mid + 1;
    }
    return l;
}
struct Answer{
    Point p[10];
    Point c[10];
    double s;
}anstem,ans;

/*--------------------------以下是应用型算法-----------------------------------*/
void ConvexHull(Point *p, int n, Point *c, int &m){
    sort(p, p + n, cmpxy);
    m = 0;

    for(int i = 0; i < n; i++){
        while(m > 1 && sgn((c[m-1] - c[m-2]) % (p[i] - c[m-1])) <= 0) m--;
        c[m++] = p[i];
    }
    int k = m;
    for(int i = n - 2; i >= 0; i--){
        while(m > k && sgn((c[m-1] - c[m-2]) % (p[i] - c[m-1])) <= 0) m--;
        c[m++] = p[i];
    }
    if(m > 1) m--;
}
void Rotate_Convex_Hull(Point *c, int m){
    int id1 = 0, id2 = 0;
    for(int i = 0; i < m; i++) if(cmpYx(c[i], c[id1])) id1 = i;
    for(int i = 0; i < m; i++) if(cmpyX(c[i], c[id2])) id2 = i;
    int nexid1 = 0, nexid2 = 0;
    Vector v1 = Vector(-1, 0), v2 = Vector(1, 0), nexv1, nexv2;
    double rad1, rad2;
    int rm1 = id1, rm2 = id2;

    ans.s = 1e100;
    while(1){
        //solve here...
        Vector norv = Norv(v1);
        double rad = angle(Line(c[id1], norv));
        int t1 = binary_find(rad);
        Point temp[100]; int numtemp = 0;
        int t = t1;
        for(int i = 0; i < 10; i++){
            int id = data[t].id;
            temp[++numtemp] = seg[id].p1, temp[++numtemp] = seg[id].p2;
            t = t == numdata? 1 : t+1;
        }
        t = t1;
        for(int i = 0; i < 10; i++){
            int id = data[t].id;
            temp[++numtemp] = seg[id].p1, temp[++numtemp] = seg[id].p2;
            t = t == 1? numdata : t-1;
        }
        Point p = c[id1] + v1 * 1e9, p1, p2;
        double mini = 1e18;
        for(int i = 1; i <= numtemp; i++){
            double d1 = Dis(temp[i], Line(p, norv));
            if(d1 < mini){
                mini = d1;
                p1 = temp[i];
            }
        }
        p = c[id1] - v1 * 1e9;
        mini = 1e18;
        for(int i = 1; i <= numtemp; i++){
            double d1 = Dis(temp[i], Line(p, norv));
            if(d1 < mini){
                mini = d1;
                p2 = temp[i];
            }
        }

        int nump = 0, mt;
        anstem.p[nump++] = Line(c[id1], v1) / Line(p1, norv);
        anstem.p[nump++] = Line(c[id2], v2) / Line(p1, norv);
        anstem.p[nump++] = Line(c[id1], v1) / Line(p2, norv);
        anstem.p[nump++] = Line(c[id2], v2) / Line(p2, norv);
        ConvexHull(anstem.p, 4, anstem.c, mt);
        anstem.s = Area_Polygon(anstem.c, mt);
        if(anstem.s < ans.s) ans = anstem;

        //solve done

        nexid1 = id1 == m-1? 0 : id1+1;
        nexid2 = id2 == m-1? 0 : id2+1;
        nexv1 = c[nexid1] - c[id1];
        nexv2 = c[nexid2] - c[id2];
        rad1 = angle(v1, nexv1);
        rad2 = angle(v2, nexv2);

        if(sgn(rad1 - rad2) < 0){
            v1 = nexv1;
            id1 = nexid1;
            v2 = Rotate(v2, rad1);
        }
        else if(sgn(rad2 - rad1) < 0){
            v2 = nexv2;
            id2 = nexid2;
            v1 = Rotate(v1, rad2);
        }
        else{
            v1 = nexv1;
            id1 = nexid1;
            v2 = nexv2;
            id2 = nexid2;
        }

        if(id1 == rm1 && id2 == rm2) break;
    }
}

Point p[N], c[N];
int main(){
    //freopen("/Users/peteryuanpan/data.txt","r",stdin);

    n = read();
    for(int i = 0; i < n; i++) p[i] = readPoi();

    ConvexHull(p, n, c, m);
// for(int i = 0; i < m; i++){
// printf("%.3f %.3f\n",c[i].x,c[i].y);
// }

    numdata = 0;
    for(int i = 0; i < m; i++){
        int nex = i == m-1 ? 0 : i + 1;
        seg[i].p1 = c[i], seg[i].p2 = c[nex];
        ++numdata;
        data[numdata].id = i;
        data[numdata].rad = angle(Line(c[i], c[nex] - c[i]));
    }
    sort(data + 1, data + 1 + numdata, cmp1);

    Rotate_Convex_Hull(c, m);

    for(int i = 0; i < 4; i++){
        printf("%.10f %.10f\n",ans.c[i].x,ans.c[i].y);
    }

    return 0;
}

你可能感兴趣的:(几何,二分,凸包,旋转卡壳)