Gym - 102460L Largest Quadrilateral(几何-凸包+旋转卡壳求最大的四边形面积)

题目链接:点击查看

题目大意:在笛卡尔坐标系上给出 n 个点,要求选出四个点,使得组成的四边形面积最大,求出这个最大的面积,注意此处组成的四边形不是严格意义上的四边形,只需要选四个点就行

题目分析:首先猜也能猜出来四边形的四个点在凸包上肯定是最优的,所以我们可以 nlogn 求出凸包,不难想到的一个 n^3 的算法就是,n * n 去枚举对角线,这样就能将四边形划分为两个三角形,然后 O( n ) 去枚举三角形另一个的顶点,在对角线的两侧分别找到一个点使得这两个三角形的面积最大,维护一下这个面积的最大值即可

显然 n^3 是不可行的,又因为枚举的对角线与所选取的三角形的第三个顶点之间具有协同性,所以可以用旋转卡壳将时间复杂度降低为 n^2

有些细节需要注意一下,如果凸包内的点小于等于 2 ,那么说明所有的点都共线,此时答案显然为 0 ,如果凸包大小为 3 的话,那么第四个点肯定是要在凸包内求,O( n ) 枚举一下即可,当凸包大小大于等于 4 就是一般情况了,旋转卡壳即可

代码:
 

#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define double LL
using namespace std;
      
typedef long long LL;
      
typedef unsigned long long ull;
      
const int inf=0x3f3f3f3f;
    
const int N=5e3+100;
 
// `计算几何模板`
int sgn(double x){
    if(x==0)return 0;
    if(x < 0)return -1;
    else return 1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;
        y = _y;
    }
    void input(){
        scanf("%lld%lld",&x,&y);
    }
    void output(){
        printf("%lld %lld\n",x,y);
    }
    bool operator == (Point b)const{
        return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
    }
    bool operator < (Point b)const{
        return sgn(x-b.x)== 0?sgn(y-b.y)<0:x 0;
        }
    };
    //`进行极角排序`
    //`首先需要找到最左下角的点`
    //`需要重载号好Point的 < 操作符(min函数要用) `
    void norm(){
        Point mi = p[0];
        for(int i = 1;i < n;i++)mi = min(mi,p[i]);
        sort(p,p+n,cmp(mi));
    }
    //`得到凸包`
    //`得到的凸包里面的点编号是0$\sim$n-1的`
    //`两种凸包的方法`
    //`注意如果有影响,要特判下所有点共点,或者共线的特殊情况`
    //`测试 LightOJ1203  LightOJ1239`
    void getconvex(polygon &convex){
        sort(p,p+n);
        convex.n = n;
        for(int i = 0;i < min(n,2);i++){
            convex.p[i] = p[i];
        }
        if(convex.n == 2 && (convex.p[0] == convex.p[1]))convex.n--;//特判
        if(n <= 2)return;
        int &top = convex.n;
        top = 1;
        for(int i = 2;i < n;i++){
            while(top && sgn((convex.p[top]-p[i])^(convex.p[top-1]-p[i])) <= 0)
                top--;
            convex.p[++top] = p[i];
        }
        int temp = top;
        convex.p[++top] = p[n-2];
        for(int i = n-3;i >= 0;i--){
            while(top != temp && sgn((convex.p[top]-p[i])^(convex.p[top-1]-p[i])) <= 0)
                top--;
            convex.p[++top] = p[i];
        }
        if(convex.n == 2 && (convex.p[0] == convex.p[1]))convex.n--;//特判
        convex.norm();//`原来得到的是顺时针的点,排序后逆时针`
    }
};
//`AB X AC`
double cross(Point A,Point B,Point C){
    return (B-A)^(C-A);
}
 
int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
    int w;
    cin>>w;
    while(w--)
    {
        polygon p,q;
        int n;
        scanf("%d",&n);
        p.input(n);
        p.getconvex(q);
        if(q.n<=2)
            puts("0");
        else if(q.n==3)
        {
            LL ans=0;
            int cnt[3]={0};//统计凸包上每个点出现的次数 
            for(int i=0;i

 

你可能感兴趣的:(几何)