• 关于graham扫描法求凸包的小记


    1、首先,凸包是啥:

    若是在二维平面上,则一般的,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有的点。

    ───────────────────────────────────────────────────────────────────────────────────────────────────────────

    2、那么,如何通过某种算法求二维平面上的凸包呢?

    有Graham扫描法(Graham scan algorithm),复杂度O(nlogn)。

    话不多说,先上当年大佬的论文……

    呃,可以看到,这个标题是非常的酷嗷,对于有限平面点集的凸包计算的高效算法,划重点。

    给一个平面点集S,标号为s1~sn,据说我们经常对找它的凸包感兴趣(真的吗……我怎么从来没感兴趣过……)

    然后graham教授就给了我们一种炫酷的五步法,来求凸包。

    第一步:

      目标是找个在凸包内部的点P。

      我们对集合S三个点三个点进行检测,检测它们是否共线:

        若共线,扔掉中点;

        若不共线,就选这三个点所组成的三角形的质心作为点P。

    第二步:

      以P为原点,任意一个方向为θ=0轴,建立一个极坐标系;

      对集合S中的每个点si,都按这个坐标系表示一下它们的坐标。

    第三步:

      现在每个点都有坐标 r ∠ θ ,我们对这些点,按照θ的升序进行排序。

    第四步:

      如果有某两个点的角度相等,就删掉r较小的那个点,因为它显然不可能是凸包边界上的点。

      另外呢,所有r=0的,也可以删了,反正也impossible。

      then,重新给还存在着的点编号,新的集合记为S'。

               

     

    第五步:

      对于S'中连续的三个点k,k+1,k+2,如图2,有两种可能:

        1)α + β ≥ π,看图,就很容易知道,这个点k+1,显然不可能是凸包边界上的点了;

          回到步骤五,重新选择点k-1,k,k+2作为新的三个点;

        2)α + β < π,就回到步骤五,选择点k+1,k+2,k+3作为新的三个点;

          相当于往前进。

    原文件:https://files.cnblogs.com/files/dilthey/graham%E6%89%AB%E6%8F%8F%E6%B3%95.pdf

    ───────────────────────────────────────────────────────────────────────────────────────────────────────────

    3、那么放到程序中,具体如何实现呢?

    我们保留“对于三个点,判断角α、β和是否小于180度,并且进行相应的前进退后”的思想,不过对于选取原点的方法进行一定的修改。

      ①找到点集S中纵坐标最小的点(如果y坐标相同,则选其中横坐标最小的),作为原点P0

      ②计算其他所有点的辐角,并且将他们按从小到大排序,如果遇到辐角相同的一些点,则按与原点距离从小到大排序,记为P1~Pn

      ③建栈,入栈P0,P1,P2

      ④选取一个点Pi(i初始值为3),前往步骤⑤;

      ⑥获得栈顶点和次栈顶点Pk,Pk-1

        进行判定:如果 Pk-1 -> P-> P是右转的(其实就是α + β ≥ π),就弹出栈顶元素,并且返回步骤⑥;

             如果是左转的(α + β < π),就入栈点Pi,并且i+=1,返回步骤④;

    ───────────────────────────────────────────────────────────────────────────────────────────────────────────

    4、代码模板:

    #include<bits/stdc++.h>
    #define MAX 10005
    #define eps 1e-6
    using namespace std;
    struct Point{
        double x,y;
        Point(double tx=0,double ty=0):x(tx),y(ty){}
    }p[MAX];
    typedef Point Vctor;
    Vctor operator - (Point A,Point B){return Vctor(A.x-B.x,A.y-B.y);}
    int dcmp(double x)
    {
        if(fabs(x)<eps) return 0;
        else return (x<0)?(-1):(1);
    }
    //叉积
    double Cross(Vctor A,Vctor B){return A.x*B.y-A.y*B.x;}
    //距离
    double dist(Point p1,Point p2){return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));}
    bool cmp(Point p1,Point p2)
    {
        double tmp=Cross(p1-p[0],p2-p[0]);
        if(!dcmp(tmp)) return dist(p[0],p1)<dist(p[0],p2);
        else return tmp>0;
    }
    vector<Point> graham_scan(int n)
    {
        vector<Point> ans;
        
        if(n<=0) return ans;
        if(n<=2)//当只有1或2个点时
        {
            if(n==2 && (p[1].y<p[0].y || (p[1].y == p[0].y && p[1].x < p[0].x)) ) swap(p[0],p[1]);
            for(int i=0;i<n;i++) ans.push_back(p[i]);
            return ans;
        }
        
        int idx=0;
        for(int i=1;i<n;i++)//选出Y坐标最小的点,若Y坐标相等,选择X坐标小的点
        {
            if(p[i].y<p[idx].y || (p[i].y == p[idx].y && p[i].x < p[idx].x)) idx=i;
        }
        swap(p[0],p[idx]);
        sort(p+1,p+n,cmp);
        for(int i=0;i<=2;i++) ans.push_back(p[i]);
        int top=2;
        for(int i=3;i<n;i++)
        {
            while(top>0 && Cross(p[i]-ans[top-1],ans[top]-ans[top-1]) >= 0)
            {
                ans.pop_back();
                top--;
            }
            ans.push_back(p[i]);
            top++;
        }
        return ans;
    }
  • 相关阅读:
    JVM——类加载
    Java IO输入输出
    核心标签库和el
    request对象
    安装tomcat
    安装mongodb
    MySQL在简单命令行操作
    安装MySQL
    Java几种常见的异常类型
    Java简单正则表达式写爬虫
  • 原文地址:https://www.cnblogs.com/dilthey/p/7763909.html
Copyright © 2020-2023  润新知