【计算几何&凸包】SCOI2007最大土地面积

题目描述

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


分析:

比较基础的计算几何代码复习题(误)
首先,很容易证明一点:
所选择的点一定位于凸包上。
因为如果存在最大四边形使得点不全在凸包上,可以将不在凸包上的点外延,得到更大的凸包:
如下图:
这里写图片描述
所选择的四边形中:左上角的点不在凸包上,那么如果那个点选择黄色区域的任意一点,都可以得到一个更大的凸包:
这里写图片描述
很显然,这个四边形就不可能是最大的。

得到这个性质以后,我们选择的范围大大缩小,不过数量级没有变化。

再来研究求解的过程:
一种很巧妙的思路是,暴力枚举两个凸包上的点作为四边形的对角线,再在这条线两侧各选择一个点,使得该点与这条线所构成的三角形面积尽量大。
这样做的优点是:可以将问题分解为两个互不干扰的相同的子问题,使问题变得简单。
接下来有两种方法:

Solution A:

不难发现,如果要在某一侧选一个点,使得该点与这条线构成的三角形面积最大:假设这条线为底,因为这条线的长度已经确定,我们只需要使得它的高尽量大即可。
这里写图片描述
很容易发现:在凸包上,这样的点组成的高线长是一个单峰函数。所以只要在这些点中三分,就可以在 O(log n) O ( l o g   n ) 复杂度内找到最远点。
总的复杂度为 O(n2log n) O ( n 2 l o g   n )
//不过我写都没写这种方法。

Solution B

继续研究这个单峰函数:
假设:能够使枚举的对角线在有一公共点的情况下有序枚举(如图)。
这里写图片描述
在这种情况下,结合之前的结论(单峰函数),我们会发现一点:对于这些线,所有的波峰的取值也是有序的(如图)
这里写图片描述
对应关系:
1-D,2-D,3-E,4-F,5-F
有了这个性质,我们就不用再三分了。
我们根据上一次的波峰,比较它与它靠后一个点,与新的线所组成的三角形面积的大小。这样一来,复杂度也降低了,总的复杂度: O(n2) O ( n 2 )
这个思路看似十分巧妙,其实是根据旋转卡壳的实现思路转化而来的,如果看作是为旋转卡壳作思想准备,这是一道不错的好题。
除此之外,这就是一道不折不扣的计算几何代码复习题了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 2010
using namespace std;
struct node{
    double x,y;
    node(){}
    node(double xx,double yy):x(xx),y(yy) {}
    node operator + (const node &a) const {
        return node(x+a.x,y+a.y);
    }
    node operator - (const node &a) const {
        return node(x-a.x,y-a.y);
    }
    node operator * (const double &t) const {
        return node(x*t,y*t);
    }
    double operator *(const node &a) const{
        return x*a.x+y*a.y;
    } 
    double operator ^(const node &a) const{
        return x*a.y-y*a.x;
    }
    bool operator < (const node &a) const{
        return x<a.x||(x==a.x&&y<a.y);
    }
}p[MAXN],l1[MAXN];
stack<node> s1,s;
double ans;
int n,cnt1;
void solve(node a1[],int &cnt){
    s.push(p[1]);
    s1.push(p[1]);
    s1.push(p[2]);
    for(int i=3;i<=n;i++){
        while(!s.empty()&&((p[i]-s.top())^(s1.top()-s.top()))<=0){
            s.pop();
            s1.pop();
        }
        s.push(s1.top());
        s1.push(p[i]);
    }
    while(!s1.empty()){
        a1[++cnt]=s1.top();
        s1.pop();
    }
    while(!s.empty())
        s.pop();
}
double sum(node a,node b,node c){
    return fabs((b-a)^(c-a));
}
bool cmp(node a,node b){
    return b<a;
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    SF("%d",&n);
    for(int i=1;i<=n;i++)
        SF("%lf%lf",&p[i].x,&p[i].y);
    sort(p+1,p+1+n); 
    solve(l1,cnt1);
    sort(p+1,p+1+n,cmp);
    cnt1--;
    solve(l1,cnt1);
    cnt1--;
    /*for(int i=1;i<=cnt1;i++)
        PF("{%.2f %.2f}\n",l1[i].x,l1[i].y);*/
    /*node t1=node(0,0);
    node t2=node(1,1);
    node t3=node(2,0);
    PF("{%.3f}\n",sum(t1,t2,t3));*/
    for(int i=1;i<=cnt1;i++){
        int st1=i%cnt1+1;
        int j=(i+1)%cnt1+1;
        int st2=(j+1)%cnt1+1;
        for(;j%cnt1+1!=i;j=j%cnt1+1){
            while(st1%cnt1+1!=j&&sum(l1[i],l1[st1],l1[j])<sum(l1[i],l1[st1%cnt1+1],l1[j]))
                st1=st1%cnt1+1;
            while(st2%cnt1+1!=i&&sum(l1[i],l1[st2],l1[j])<sum(l1[i],l1[st2%cnt1+1],l1[j]))
                st2=st2%cnt1+1;
            //PF("[%d(%.0f,%.0f) %d(%.0f,%.0f)-%d(%.0f,%.0f) %d(%.0f,%.0f) %.2f %.2f]\n",i,l1[i].x,l1[i].y,st1,l1[st1].x,l1[st1].y,st2,l1[st2].x,l1[st2].y,j,l1[j].x,l1[j].y,sum(l1[i],l1[st1],l1[j])/2.0,sum(l1[i],l1[st2],l1[j])/2.0);
            ans=max(ans,(sum(l1[i],l1[st1],l1[j])+sum(l1[i],l1[st2],l1[j]))/2.0);
        }
    }
    PF("%.3lf",ans);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值