题目:
给定平面上
n
n
n个点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),找四个点组成四边形,问组成的四边形的最大面积是多少。注意会有重点,四边形可以退化。
(
4
≤
n
≤
4096
,
0
≤
x
i
,
y
i
≤
1
0
9
)
(4 \le n \le 4096,0 \le x_i,y_i \le 10^9)
(4≤n≤4096,0≤xi,yi≤109)
题解:
首先,能取到凸四边形肯定不会取凹四边形。
先用点集搞出凸包,如果凸包上少于三个点,那么只能退化为一条线,答案为0;如果凸包上只有三个点,而其他点都在凸包内部,所以只能组成凹四边形或者退化成三角形;如果至少有四个点,那么说明可以组成凸四边形,显然面积最大的四边形的四个点都在凸包上。考虑枚举四边形的一条对角线,那么四边形就可以看成是两个三角形拼起来的,我们使这两个三角形的面积最大即可。先看一边,考虑怎么使三角形面积最大,底边已经确定,所以我们找出一个点,到底边的距离最大即可,借鉴一下旋转卡壳的思路,由于凸包的凸性,所以一边的点到枚举的对角线的距离是一个単峰函数,所以当我们确定对角线的其中一个点,遍历枚举另一个点时,也就是对角线在绕一个点旋转时,到对角线距离最大的点也会单调的旋转,所以搞双指针维护即可。
由于给定的都是整点,而要求的是面积,所以面积或是一个整数,或是一个.5的小数。那么我们在算三角形面积的时候不除以2,最后特判整数或.5即可。实现的时候全用long long,无精度损失。
复杂度: O ( n 2 ) O(n^2) O(n2)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;
#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=5005;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Point{
ll x,y;
Point(ll x=0,ll y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator-(Vector a,Vector b){
return Vector(a.x-b.x,a.y-b.y);
}
ll Cross(Vector a,Vector b){
return a.x*b.y-a.y*b.x;
}
ll Area(Point a,Point b,Point c){
return abs(Cross(b-a,c-a));
}
bool operator<(const Point &a,const Point &b){
if(a.x==b.x)return a.y<b.y;
else return a.x<b.x;
}
int used[maxn],stk[maxn];
int Andrew(Point *p,int n,Point *h,int *id){
int tp=0;
sort(p,p+n);
for(int i=0;i<n;i++)used[i]=0;
stk[++tp]=0;
for(int i=1;i<=n-1;i++){
while(tp>=2&&Cross(p[stk[tp]]-p[stk[tp-1]],p[i]-p[stk[tp]])<=0){
used[stk[tp--]]=0;
}
used[i]=1;
stk[++tp]=i;
}
int tmp=tp;
for(int i=n-2;i>=0;i--){
if(!used[i]){
while(tp>tmp&&Cross(p[stk[tp]]-p[stk[tp-1]],p[i]-p[stk[tp]])<=0){
used[stk[tp--]]=0;
}
used[i]=1;
stk[++tp]=i;
}
}
for(int i=1;i<=tp;i++){
h[i-1]=p[stk[i]];
id[i-1]=stk[i];
}
return tp-1;
}
ll RotateCalipers(Point *p,int n){
ll res=0;
for(int i=0;i<n;i++){
int j=(i+1)%n,jj=(i+3)%n;
for(int k=(i+2)%n;k!=i;k=(k+1)%n){
while(Area(p[i],p[j],p[k])<Area(p[i],p[(j+1)%n],p[k]))j=(j+1)%n;
while(Area(p[i],p[jj],p[k])<Area(p[i],p[(jj+1)%n],p[k]))jj=(jj+1)%n;
res=max(res,Area(p[i],p[j],p[k])+Area(p[i],p[jj],p[k]));
}
}
return res;
}
int T,n;
Point p[maxn],h[maxn];
int id[maxn];
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lld%lld",&p[i].x,&p[i].y);
}
int num=Andrew(p,n,h,id);
if(num<3){
puts("0");
continue;
}
ll ans=0;
if(num==3){
for(int i=0;i<n;i++){
int flag=0;
for(int j=0;j<num;j++){
if(i==id[j]){
flag=1;
break;
}
}
if(flag)continue;
ll s=Area(h[0],h[1],h[2]);
ll mn=min(Area(p[i],h[1],h[2]),min(Area(h[0],p[i],h[2]),Area(h[0],h[1],p[i])));
ans=max(ans,s-mn);
}
}
else{
ans=RotateCalipers(h,num);
}
if(ans%2==0)printf("%lld\n",ans/2);
else printf("%lld.5\n",ans/2);
}
return 0;
}