给定一组点集,选择三点组成三角形,且面积最大。
肯定要求出这组点的凸包,接下来就不能想当然的三重循环枚举三点,这样很容易超时。这时就要用到神奇的旋转卡壳法。
取凸包的三点i,j,k。先固定i,j。逆时针(当然你也可以顺时针)变换k,你会发现i,j,k三点组成的三角形面积具有单峰性。即如果第一次找到Area(i,j,k+1)<Area(i,j,k),那么此时就是在i,j固定下的形成最大面积的k点了。这样两重循环枚举i,j,时间复杂度为o(n^2)。也许有人会认为不是也要找k吗?时间复杂度为(n^3)才对。非也,如果你认真观察的话,你会发现在枚举j时,k并不需要从初始枚举,因为j在逆时针移动时,k也在逆时针移动。所以只需从上一个k开始接着逆时针找k。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct Point
{
double x,y;
};
bool mult(Point curr, Point end, Point begin)
{
return (curr.x-begin.x)*(end.y-begin.y)
>=(end.x-begin.x)*(curr.y-begin.y);
}
bool comp(const Point &a,const Point &b)
{
return a.y<b.y||(a.y==b.y&&a.x<b.x);
}
int graham(Point pnt[], int n, Point res[])
{
int i,len,top=1;
sort(pnt,pnt+n,comp);
if(n==0)
{
return 0;
}
res[0]=pnt[0];
if(n==1)
{
return 1;
}
res[1]=pnt[1];
if(n==2)
{
return 2;
}
res[2]=pnt[2];
for(i=2;i<n;i++)
{
while(top&&mult(pnt[i],res[top],res[top-1]))
{
top--;
}
res[++top]=pnt[i];
}
len=top;
res[++top]=pnt[n-2];
for(i=n-3;i>=0;i--)
{
while(len!=top&&mult(pnt[i],res[top],res[top-1]))
{
top--;
}
res[++top]=pnt[i];
}
return top;
}
double Area(Point a,Point b,Point c)
{
return fabs((a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y))/2;
}
int main()
{
int n;
Point s[50005],res[50005];
while(scanf("%d",&n)==1&&n>0)
{
for(int i=0;i<n;i++)
{
scanf("%lf%lf",&s[i].x,&s[i].y);
}
int m=graham(s,n,res);
double ans=0;
int p1,p2,p3;
for(p1=0;p1<m;p1++)
{
p2=(p1+1)%m;
p3=(p2+1)%m;
ans=max(ans,Area(res[p1],res[p2],res[p3]));
while(Area(res[p1],res[p2],res[(p3+1)%m])>Area(res[p1],res[p2],res[p3]))
{
//ans=max(ans,Area(res[p1],res[p2],res[p3+1]));
p3=(p3+1)%m;
}
while((p1-p3+m)%m>1)
{
ans=max(ans,Area(res[p1],res[p2],res[p3]));
while(Area(res[p1],res[p2],res[(p3+1)%m])>Area(res[p1],res[p2],res[p3]))
{
//ans=max(ans,Area(res[p1],res[p2],res[p3+1]));
p3=(p3+1)%m;
}
p2=(p2+1)%m;
//ans=max(ans,Area(res[p1],res[p2],res[p3]));
}
}
printf("%.2lf\n",ans);
}
return 0;
}