bzoj1069 [SCOI2007]最大土地面积(凸包+旋转卡壳)

83 篇文章 0 订阅
7 篇文章 0 订阅

bzoj1069 [SCOI2007]最大土地面积

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=1069

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

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

题解:
首先,发现这四个点肯定都在凸包上,因为倘若一个点不在凸包上,肯定可以让凸包上一个点代替它,使面积更大。
暴力枚举4个点肯定是不行的。
于是考虑枚举对角线,然后寻找对角线两侧可以取到的最大三角形。
如果固定一个点,顺时针枚举对角线的另外一个点,发现与他们组成四边形的另两个点也是在顺时针移动的。
(想像用与枚举的整条对角线在两侧去卡这个凸包)
于是这是O(n^2)的。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define Poi Vec
using namespace std;
const double EPS=1e-8;
const int N=2005;
struct Vec
{
    double x,y;
    Vec(double x=0.0,double y=0.0):x(x),y(y) {}
}P[N],cvx[N];
Vec operator+(const Vec &A,const Vec &B) {return Vec(A.x+B.x,A.y+B.y);}
Vec operator-(const Vec &A,const Vec &B) {return Vec(A.x-B.x,A.y-B.y);}
Vec operator*(const Vec &A,double s) {return Vec(A.x*s,A.y*s);}
Vec operator/(const Vec &A,double s) {return Vec(A.x/s,A.y/s);}
double cross(const Vec &A,const Vec &B) {return A.x*B.y-B.x*A.y;}
double dot(const Vec &A,const Vec &B) {return A.x*B.x+A.y*B.y;}
bool cmp(const Vec &A,const Vec &B) {return (A.x!=B.x)?A.x<B.x:A.y<B.y;}
bool onleft(Poi A,Poi B,Poi C) {return cross(B-A,C-A)>0;}
int n,nxt[N];
int getcvx()
{
    sort(P,P+n,cmp);
    int t=0; cvx[0]=P[0]; 
    for(int i=1;i<n;i++)
    {
        while(t>0&&!onleft(cvx[t-1],cvx[t],P[i])) t--;
        cvx[++t]=P[i];
    }
    int h=t;
    for(int i=n-2;i>=0;i--)
    {
        while(t>h&&!onleft(cvx[t-1],cvx[t],P[i])) t--;
        cvx[++t]=P[i];
    }
    if(n>1) t--;
    return t+1;
}
double getarea(Poi A,Poi B,Poi C) {return fabs(cross(B-A,C-A))/2.0;}
double getpoly()
{
    if(n<=2) return 0;
    else if(n==3) return getarea(cvx[0],cvx[1],cvx[2]);
    double ans=0.0;
    for(int i=0;i<n;i++) nxt[i]=(i+1)%n;
    for(int i=0;i<n;i++)
    {
        for(int j=((i+1)%n+1)%n,a=(i+1)%n,b=(j+1)%n;(j+1)%n!=i;j=(j+1)%n)///
        {
            while(getarea(cvx[i],cvx[a],cvx[j])<getarea(cvx[i],cvx[(a+1)%n],cvx[j])) a=(a+1)%n;
            while(getarea(cvx[j],cvx[b],cvx[i])<getarea(cvx[j],cvx[(b+1)%n],cvx[i])) b=(b+1)%n;
            ans=max(ans,getarea(cvx[i],cvx[a],cvx[j])+getarea(cvx[j],cvx[b],cvx[i]));
        }
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) {double x,y; scanf("%lf%lf",&x,&y); P[i]=Vec(x,y);}
    n=getcvx();
    double ans=getpoly();
    printf("%0.3lf\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值