【题目大意】在某块平面土地上有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);
}