【凸包+旋转卡壳思想】 BZOJ 1069

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

【数据范围】4≤n≤2000, |x|,|y|<=100000

【思路】毫无疑问,这四个点肯定在凸包上。然后可以枚举对角线,然后再枚举对角线两边的点。当对角线定下来了以后,对于一侧的三角形,可以发现它的面积是单峰的——三角形顶点沿一个方向移动的时候,面积先增后减。那么当三角形面积开始减小的时候就可以停止枚举了。这时候,我们就得到了此对角线下的最优解。

比如说这样,这时我们的对角线是红色的线段,蓝色的四边形就对应最优解。

然后移动这个对角线的一个顶点。如下:

我们发现这时可以利用前一个的最优解!因为前面保证了是单增的。

然后就可以从前一个的最优解开始枚举,过程如下:

思想和旋转卡壳类似。

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=10000;
struct point{
    double x,y;
    point(double _x=0,double _y=0){x=_x,y=_y;}
    friend inline point operator +(const point &a,const point &b){
        return point(a.x+b.x,a.y+b.y);
    }
    friend inline point operator -(const point &a,const point &b){
        return point(a.x-b.x,a.y-b.y);
    }
    friend inline double operator *(const point &a,const point &b){
        return (a.x*b.y-a.y*b.x);
    }
}p[maxn],st[maxn];
double ang[maxn],ans=0,parta=0,partb=0;
int id=1,top=0,n;
inline double dist(point a,point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline bool cmp(const point &a,const point &b){
    double K=(a-p[1])*(b-p[1]);
    return (K==0)?(dist(a,p[1])<dist(b,p[1])):(K>0);
}
inline int add(int a){return ((a+1)>top)? a+1-top : a+1;}
inline void Graham(){
    swap(p[id],p[1]);
    sort(p+2,p+n+1,cmp);
    st[++top]=p[1];
    for(int i=2;i<=n;++i){
        while((top>=3)&&((p[i]-st[top-1])*(st[top]-st[top-1])>=0))
            top--;
        st[++top]=p[i];
    }
    st[top+1]=st[1];
}

//i和j是对角线顶点,k和l是两边的顶点。
inline void rotating_calipers(){
	int i,j,k,l;
    for(i=1;i<=top;++i){
    	k=i,l=add(i);
	    for(j=i+1;j<=top;++j)
		{
			while((st[add(k)]-st[i])*(st[j]-st[i])>(st[k]-st[i])*(st[j]-st[i])) k=add(k);
			while((st[j]-st[i])*(st[add(l)]-st[i])>(st[j]-st[i])*(st[l]-st[i])) l=add(l);
			ans=max(ans,(st[j]-st[i])*(st[l]-st[i])+(st[k]-st[i])*(st[j]-st[i]));
		}
	}
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%lf%lf",&p[i].x,&p[i].y);
        if(p[i].y>p[id].y||((p[i].y==p[id].y)&&(p[i].x<p[id].x))) id=i;
    }
    Graham(),rotating_calipers();
    printf("%.3f\n",ans/2);
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/83144864