zoukankan      html  css  js  c++  java
  • 凸包-Graham扫描法

    凸包

    简介

    凸包 ((Convex Hull)) 是一个计算几何(图形学)中的概念。

    在一个实数向量空间(V)中,对于给定集合(X),所有包含X的凸集交集(S)被称为(X)的凸包。(X)的凸包可以用(X)内所有点((X1,...Xn))凸组合来构造.

    在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。

    用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。

    性质

    凸包用最小的周长围住了给定的所有点;

    如果一个凹多边形围住了所有的点,它的周长一定不是最小;

    如下图。根据三角不等式,凸多边形在周长上一定是最优的;


    前置知识

    叉积

    两个向量 (A(x1,y1)) , (B(x2,y2))

    的叉积为 (x1 imes y2−x2 imes y1x1 imes y2−x2 imes y1) ;

    先放出代码

    inline ll cross(node p,node a,node b)
    {
    	ll a1,b1,a2,b2;
    	a1=a.x-p.x; b1=a.y-p.y;
    	a2=b.x-p.x; b2=b.y-p.y;
    	return a1*b2-a2*b1;
    }
    

    对于这个函数如果函数值为正数说明,(B)(A) 的逆时针方向;

    如果函数值为负数说明,(B)(A) 的顺时针方向;

    如果函数值为 (0) 说明, (A) ,(B) ,(P) 三点共线;


    求三点的三角形面积

    (S= frac{1}{2} abs((x1−x3) imes (y2−y3)−(x2−x3) imes (y1−y3))) ;


    算法:Graham扫描法

    复杂度 : n (log (n)) ;

    思路

    大概做法是找到一个点,朝一个方向不断加点,保证所有的点在里面并且是一个凸多边形;

    具体找到一个纵坐标最小的点作为基点,若有多个则选取横坐标最小的点;

    然后以基点为原点构造平面直角坐标系,按每个点到原点的直线与 x 轴的夹角 从小到大排序;

    排序可以利用叉积判断点位置关系;

    如图


    之后利用叉积性质,例如:

    栈元素: (p0) , (p1) ; 利用叉积判断 线段(p0p1) 是否在 (p1p2) 的逆时针方向; 满足入队;

    栈元素: (p0) , (p1) , (p2) ; 判断 线段(p1p2) 是否在 (p2p3) 的逆时针方向; 不满足 (p2) 出队;

    。。。。。。

    简单来说就是每次去掉那个往内部凹的角;

    动图GIF


    代码

    inline ll cross(node p,node a,node b)//叉积公式 
    {
        ll a1,b1,a2,b2;
        a1=a.x-p.x; b1=a.y-p.y;
        a2=b.x-p.x; b2=b.y-p.y;
        return a1*b2-a2*b1;
    }
    inline ll cmp(node x,node y)
    {
        ll sum=cross(p[1],x,y);
        if(sum>0) return 1;//sum 大于零说明,y 在 x 的逆时针方向 
        if(sum<0) return 0;//sum 小于零说明,y 在 x 的顺时针方向 
        return dis(x,p[1])<dis(y,p[1]); //如果三点共线,近的排在前面 
    }
    inline void convex_hull()
    {
        node a=(node){1<<30,1<<30};
        ll id=0;
        for(re ll i=1;i<=n;i++)
        if(p[i].y<a.y||(p[i].y==a.y&&p[i].x<a.x))
        {
            a=p[i];
            id=i;
        }//确定纵坐标最小的基点 
        swap(p[1],p[id]);
        sort(p+2,p+n+1,cmp);//除基点外 排序 
        aa[++top]=p[1];
        aa[++top]=p[2];
        for(re ll i=3;i<=n;i++)
        {
            while(top>1&&cross(aa[top],aa[top-1],p[i])>=0)//如果出现凹下去的地方 
                top--;    //退栈 
            aa[++top]=p[i];
        }
    }
    
  • 相关阅读:
    使用公用表表达式的递归查询
    cocos2d-x 精灵的创建和基本使用
    全栈project师的毁与誉
    使用zTree和json构建简单树节点
    使用贝赛尔曲线画扇形、圆形、弧线、多边形,实现App下载时的动画效果demo
    UIBezierPathStudyDemo
    iOS-swift环形进度指示器+图片加载动画
    IOS之以UIBezierPath绘制饼状图
    Swift之UIBezierPath
    swift app中展示折线图, 饼状图, 柱状图等数据图表
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/14415048.html
Copyright © 2011-2022 走看看