poj 2079 Triangle (二维凸包旋转卡壳)

Time Limit: 3000MS   Memory Limit: 30000KB   64bit IO Format: %I64d & %I64u
Submit Status

Description

 

 

 

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 −10^4 ≤ xi, yi ≤ 10^4 for all i = 1 . . . n.

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

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 output
0.50
27.00
题意:给出N个点,从中选出3个点,使其面积达到最大。
题解:我们知道这三个点肯定在凸包上,我们求出凸包之后不能枚举,因为题目n比较大,枚举的话要O(n^3)的数量级,所以采用旋转卡壳的做法:
首先确定i,j,对k进行循环,知道找到第一个k使得cross(i,j,k)>cross(i,j,k+1),如果k==i进入下一次循环。
对j,k进行旋转,每次循环之前更新最大值,然后固定一个j,同样找到一个k使得cross(i,j,k)>cross(i,j,k+1)。对j进行++操作,继续进行下一次,
直到j==k为止。
#include<cstdio>
#include<cmath>
#include<algorithm>
#define MAX 50010
using namespace std;
struct Point{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
Point P[MAX],ch[MAX];
typedef Point Vector;
Vector operator - (Point A,Point B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
bool operator <(const Point &a,const Point &b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
int ConvexHull(Point *p,int n)
{
    sort(p,p+n);
    int m=0;
    for(int i=0;i<n;i++)
    {
        while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-2;i>=0;i--)
    {
        while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    if(n>1) m--;
    return m;
}
double rotating_calipers(int n)
{
    double ans=0,tmp;
    int i,j,k,kk;
    n++;
    for(int i=0;i<n;i++)
    {
        j=(i+1)%n;
        k=(j+1)%n;
        while(k!=i&&j!=i)
        {
            while(k!=i&&Cross(ch[i]-ch[j],ch[j]-ch[k])<Cross(ch[i]-ch[j],ch[j]-ch[k+1]))
            k=(k+1)%n;
            tmp=Cross(ch[i]-ch[j],ch[j]-ch[k]);
            ans=max(tmp,ans);
            j=(j+1)%n;
        }
        if(k==i)
            continue;
        /*kk=(k+1)%n; //kk是标记j的 j +1以后 不能是kk 也就是说j不等于k的意思 kk!=j
        //环状的 可以遍历所有情况 一定不会忘了后期的长边的 无需顾虑
        //不存在k到i之间有一个点k0使得ijk0很大 一一对应着都比ijk小
        //为什么j==kk的时候要停止 ik这条边的意义是什么 环状可以遍历所有情况的 ik等到i变成k k变成i的时候j正好是另一半面 得证
     //固定一个顶点 找出对应着这个顶点的area最大的三角形 遍历所有顶点 所以最大的那个三角形会出现三次 while(k!=i&&kk!=j) { tmp=Cross(ch[i]-ch[j],ch[j]-ch[k]); ans=max(tmp,ans); while(k!=i&&Cross(ch[i]-ch[j],ch[j]-ch[k])<Cross(ch[i]-ch[j],ch[j]-ch[k+1])) k=(k+1)%n; j=(j+1)%n; }
*/ } return ans/2; } int main() { int n; while(scanf("%d",&n)&&n!=-1) { for(int i=0;i<n;i++) scanf("%lf%lf",&P[i].x,&P[i].y); int m=ConvexHull(P,n); double ans=rotating_calipers(m); printf("%.2f\n",ans); } return 0; }

//不能像D题poj2187一样枚举边 因为可能三个点 任意两个点不相邻

//把kk那段代码展开,会让运行时间变少100ms。时限为3000ms

//关于二维凸包旋转卡壳问题的证明还需加深理解,这道题是环状遍历,一定可以遍历所有情况的。

posted @ 2016-05-19 10:58  Ritchie丶  阅读(253)  评论(0编辑  收藏  举报