zoukankan      html  css  js  c++  java
  • 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 ",ans); } return 0; }

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

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

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

  • 相关阅读:
    一、面试准备Java知识
    SSM框架学习之Spring学习笔记
    新年过后 第一天上班
    tomcat学习笔记
    第一次写博客
    SQL Server 2005 数据类型 .NET Framework 数据类型 LINQ 数据类型
    Winform下编译Dev控件时提示license.licx文件错误
    将C#数据转化成Word文档
    得到相同的数据 雨
    SQl 2005 存储过程分页 雨
  • 原文地址:https://www.cnblogs.com/Ritchie/p/5507935.html
Copyright © 2011-2022 走看看