【POJ2079】Triangle-旋转卡壳
测试地址:Triangle
题目大意:平面上有N(N≤50000)个点,要求选出其中的3个点,使得连成的三角形面积最大,求出这个最大面积。
做法:这题需要用到旋转卡壳。
首先O(N^3)的枚举肯定是炸的,那么怎么办呢?
我们可以先求出凸包,可以证明最大的三角形顶点一定是凸包的顶点。然后枚举三角形的其中一个顶点i,然后初始化另两个顶点i,j为j=next[i],k=next[j],首先将找到一点k使得三角形ijk面积最大,然后更新最大面积,再然后将j赋值为next[j],再找到一点k使得三角形ijk面积最大,然后再更新最大面积,如此重复直到j=i。我们可以发现,k是不会往回移动的,所以总复杂度为O(N^2),虽然看上去过不了,但是实测是可以通过全部数据的。
犯二的地方:要注意枚举的是凸包上的点,以及G++编译器输出实数要使用f而不是lf,被坑了好多遍了总记不住,好气啊。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define eps 1e-8
#define inf 1000000000
using namespace std;
int n,s[50010],t,next[50010];
double ans;
struct point
{
double x,y;
point operator - (point a) const
{
point s;
s.x=x-a.x;
s.y=y-a.y;
return s;
}
}p[50010];
double dis(point a,point b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
double multi(point a,point b)
{
return a.x*b.y-b.x*a.y;
}
bool cmp(point a,point b)
{
double s=multi(a-p[1],b-p[1]);
if (fabs(s)>eps) return s>0;
else return dis(a,p[1])<dis(b,p[1]);
}
void graham_scan()
{
double lx=inf,ly=inf;
int li;
for(int i=1;i<=n;i++)
if (p[i].x<lx||(fabs(p[i].x-lx)<=eps&&p[i].y<ly))
{
lx=p[i].x,ly=p[i].y;
li=i;
}
swap(p[1],p[li]);
sort(p+2,p+n+1,cmp);
s[1]=1;t=1;
for(int i=2;i<=n;i++)
{
while(t>1&&multi(p[i]-p[s[t-1]],p[s[t]]-p[s[t-1]])>=0) t--;
s[++t]=i;
}
}
void rotating_calipers()
{
for(int i=1;i<t;i++) next[s[i]]=s[i+1];
next[s[t]]=s[1];
for(int i=1;i<=t;i++)
{
int j=next[s[i]],k=next[j];
while(j!=s[i])
{
while(k!=s[i]&&multi(p[j]-p[s[i]],p[k]-p[s[i]])<multi(p[j]-p[s[i]],p[next[k]]-p[s[i]])) k=next[k];
ans=max(ans,multi(p[j]-p[s[i]],p[k]-p[s[i]])/2);
j=next[j];
}
}
}
int main()
{
while(scanf("%d",&n)&&n!=-1)
{
for(int i=1;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
ans=0;
graham_scan();
rotating_calipers();
printf("%.2f\n",ans);
}
return 0;
}