[poj2079]Triangle--计算几何,旋转卡壳

题目描述

Given n distinct points on a plane, your task is to find the triangle that have the maximum area, whose vertices are from the given points.

Input

The input consists of several test cases. The first line of each test case contains an integer n, indicating the number of points on the plane. Each of the following n lines contains two integer xi and yi, indicating the ith points. The last line of the input is an integer −1, indicating the end of input, which should not be processed. You may assume that 1 <= n <= 50000 and −104 <= xi, yi <= 104 for all i = 1 … n.

Output

For each test case, print a line containing the maximum area, which contains two digits after the decimal point. You may assume that there is always an answer which is greater than zero.

Sample Input

3
3 4
2 6
2 7
5
2 6
3 9
2 0
8 0
6 5
-1

Sample Output

0.50
27.00

题解

这道题目的意思就是给你一个点集,在其中选三个点,要你求出选出三个点所围成面积的最大值。

首先我们可以想到,这三个点肯定在点集的凸包上,不然的话就可以往外扩展使得面积更大。

那么怎么在凸包上求呢?

我们可以在凸包上枚举一个端点i,然后我们在利用凸包的单峰去找到那两个点。那么我们怎么从i个点转到第i+1个点呢?

我们可以发现,既然我们枚举端点是逆时针枚举的,那么在第i个点的答案指针前的那些点,在第i+1个点的时候它的答案肯定是没有答案指针优的,这个画图就可以画得出来,于是我们就可以继承第i个点的答案指针继续向逆时针方向枚举。两个指针都是这样的话,我们就可以把复杂度降到 O(n) O ( n ) ,比暴力枚举的 O(n3) O ( n 3 ) 要快了不止一点。

这道题目就讲完了,大家如果有不清楚的可以看我代码。

下面放上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 50010
using namespace std;
struct P{
    double x,y;
}p[maxn];
int n;
P operator - (P a,P b)
{
    P tmp;
    tmp.x=a.x-b.x;tmp.y=a.y-b.y;
    return tmp;
}
double operator * (P a,P b)
{
    return a.x*b.y-b.x*a.y;
}
double dist(P a,P b)
{
    P tmp=a-b;
    return sqrt(tmp.x*tmp.x+tmp.y*tmp.y);
}
bool cmp(P p1,P p2)
{
    double s=(p1-p[1])*(p2-p[1]);
    return s>0||(s==0&&dist(p1,p[1])>=dist(p2,p[1]));
}
int q[maxn],top;
bool judge(int p0,int p1,int p2)
{
    double s=(p[p1]-p[p0])*(p[p2]-p[p0]);
    return s>0||(s==0&&dist(p[p1],p[p0])>=dist(p[p2],p[p0]));
}
double S(P p1,P p2,P p3)
{
    double l1=dist(p1,p2),l2=dist(p2,p3),l3=dist(p3,p1);
    double ar=(l1+l2+l3)/2;
    return sqrt(ar*(ar-l1)*(ar-l2)*(ar-l3));
}
int main()
{
    while(~scanf("%d",&n)&&n!=-1)
    {
        memset(q,0,sizeof(q));
        top=0;
        for(int i=1;i<=n;i++)  scanf("%lf%lf",&p[i].x,&p[i].y);
        int fir=1;
        for(int i=2;i<=n;i++)
        {
            if(p[i].y<p[fir].y||(p[i].y==p[fir].y&&p[i].x<p[fir].x)){
                fir=i;
            }
        }
        swap(p[fir],p[1]);
        sort(p+2,p+n+1,cmp);
        p[n+1]=p[1];
        q[++top]=1;q[++top]=2;
        for(int i=3;i<=n+1;i++)
        {
            while(top>1&&judge(q[top-1],i,q[top])==true)  top--;
            q[++top]=i;
        }
        int k=2,l=3;
        double ans=0.0;//k,l分别是两个指针
        for(int i=1;i<top;i++)
        {
            while((p[q[k]]-p[q[i]])*(p[q[l]]-p[q[i]])<(p[q[k]]-p[q[i]])*(p[q[l+1]]-p[q[i]]))//判断答案大小
            {
                l++;
                if(l==top)  l=1; 
            }
            while((p[q[k]]-p[q[i]])*(p[q[l]]-p[q[i]])<(p[q[k+1]]-p[q[i]])*(p[q[l]]-p[q[i]]))//同上
            {
                k++;
                if(k==top)  k=1;
            }
            ans=max(ans,S(p[q[i]],p[q[k]],p[q[l]]));//修改答案
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}

谢谢大家!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值