【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
",ans);
  }
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793703.html