BZOJ 1069 [SCOI 2007] 旋转卡壳 解题报告

本文介绍了一种高效算法,用于解决给定平面上N个点时如何找出四点构成的最大多边形面积问题。通过计算几何的方法,如凸包构造与优化搜索策略,实现了O(N^2)的时间复杂度。

1069: [SCOI2007]最大土地面积

Description

在某块平面土地上有N个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成
的多边形面积最大。

Input

第1行一个正整数N,接下来N行,每行2个数x,y,表示该点的横坐标和纵坐标。

Output

最大的多边形面积,答案精确到小数点后3位。

Sample Input

5
0 0
1 0
1 1
0 1
0.5 0.5

Sample Output

1.000

HINT

数据范围 n<=2000, |x|,|y|<=100000

【解题报告】
发现这是原来做过的原题。
记得当时最纠结的就是为啥这是n方而不是n三方。
后来想通了,因为枚举接踵点是单调的

代码如下:

/**************************************************************
    Problem: 1069
    User: onepointo
    Language: C++
    Result: Accepted
    Time:280 ms
    Memory:1292 kb
****************************************************************/

#include <bits/stdc++.h>
using namespace std;

struct Point
{  
    double x,y;         
    Point(long double _ = .0,long double __ = .0):x(_),y(__) {}  
    Point operator +(const Point &a)const
    {return Point(x+a.x,y+a.y);}  
    Point operator -(const Point &a)const
    {return Point(x-a.x,y-a.y);}  
    Point operator *(double a)const
    {return Point(x*a,y*a);}
    bool operator< (const Point& _P)const
    {return y<_P.y||(y==_P.y&&x<_P.x);}
};
int n;
vector<Point> pts,cvx;
double cross(Point a,Point b)
{
    return a.x*b.y-a.y*b.x;
}
double area(Point a,Point b,Point c) 
{
    return fabs(cross(b-a,c-a))/2.0;
}
bool onleft(Point a,Point b,Point p) 
{
    return cross(b-a,p-a)>0;
}
vector<Point> convex(vector<Point> pts) 
{
    int n=(int)pts.size();
    sort(pts.begin(),pts.end());
    vector<Point>stk;
    int m=0;
    for(int i=0;i<n;++i) 
    {
        while(m>1&&!onleft(stk[m-2],stk[m-1],pts[i])) 
        {
            m--;stk.pop_back();
        }
        m++;
        stk.push_back(pts[i]);
    }
    int k=m;
    for(int i=n-2;i>=0;--i) 
    {
        while(m>k&&!onleft(stk[m-2],stk[m-1],pts[i])) 
        {
            m--;stk.pop_back();
        }
        m++;
        stk.push_back(pts[i]);
    }
    if(n>1) 
    {
        m--;
        stk.pop_back();
    }
    return stk;
}
double maxarea(vector<Point>cvx) 
{
    int n=(int)cvx.size();
    vector <int> next(n,0);
    for(int i=0;i<n;++i) next[i]=(i+1)%n;
    if(n<=2) return 0.0;
    if(n==3) return area(cvx[0],cvx[1],cvx[2]);
    double ans=0.0;
    for(int i=0;i<n;++i) 
    for(int j=next[next[i]],a=next[i],b=next[j];next[j]!=i;j=next[j]) 
    {
        while(area(cvx[i],cvx[a],cvx[j])<area(cvx[i],cvx[next[a]],cvx[j])) a=next[a];
        while(area(cvx[j],cvx[b],cvx[i])<area(cvx[j],cvx[next[b]],cvx[i])) b=next[b];
        ans=max(ans,area(cvx[i],cvx[a],cvx[j])+area(cvx[j],cvx[b],cvx[i]));
    }
    return ans;
}
int main() 
{
    int n;
    scanf("%d",&n);
    vector <Point> pts;
    for(int i=0;i<n;++i) 
    {
        double x,y;
        scanf("%lf%lf",&x,&y);
        pts.push_back(Point(x,y));
    }
    printf("%.3f\n",maxarea(convex(pts)));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值