zoukankan      html  css  js  c++  java
  • 计算几何模板(4):多边形与凸包

    ---恢复内容开始---

    比较有意思的,旋(xuan)转(zhuan)卡(qia)壳(ke),还有半平面交都在这里。


    1.多边形与凸包

    左边是一个多边形,右边是一个凸包:

    一般用按某一方向遍历整个多边形/凸包的数组/vector表示。

    2.求凸包面积

    随便找一个顶点,然后向不相邻的点连边,将凸包分成$(n-2)$个小三角形。

    然后叉积就好了。

    最后注意叉积求的是有向面积。(大力推荐逆时针遍历

    typedef vector<Point> Pol;
    double S_(Pol p)
    {
        double ret = 0;
        for(int i=1,lim=(int)p.size();i<lim-1;i++)
            ret+=((p[i]-p[0])^(p[i+1]-p[0]));
        return ret/2;
    }
    求凸包面积

    3.判断点是否在多边形内

    有一个方法(射线法):以该点为起点做一条射线,若穿过奇数次边界则点在多边形内。

    实现的时候对于顶点的地方有个技巧。我们可以遵守左包含右不包含(虽然反过来也一样),这样对于一个顶点来说,要么算两次,要么一次不算,不会影响判定。

    int P_in_Pol(Point p,Pol P)
    {
        int now = 0,n = P.size();
        for(int i=0;i<n;i++)
        {
            if(P_S(p,P[i],P[(i+1)%n]))return -1;
            int zt = dcmp((P[(i+1)%n]-P[i])^(p-P[i]));
            int d0 = dcmp(p.x-P[i].x);
            int d1 = dcmp(p.x-P[(i+1)%n].x);
            if(zt>0&&d0>=0&&d1<0)now^=1;
            if(zt<0&&d1>=0&&d0<0)now^=1;
        }
        return now;
    }
    点在多边形内

    4.建凸包

    这里的建凸包不是在普通的斜率优化$dp$里的单建上/下凸包,是给出散点建一个整体凸包。

    大体做法是,将点排序,先按$x$升序再按$y$升序。

    然后按逆时针顺序建凸包。要保证的是,凸包上先后两条边的叉积是恒为正的,相当于一条向量逆时针转了一圈。

    建完第一遍之后,我们得到的是下凸包,这时候再反着扫一遍,依然保证叉积恒为正即可。

    int build(int n,Point *p,Point *s)
    {
        sort(p,p+n);
        int m = 0;
        for(int i = 0;i < n;i++)
        {
            while(m>1&&((s[m-1]-s[m-2])^(p[i]-s[m-2]))<=0)m--;
            s[++m] = p[i];
        }
        int lim = m;
        for(int i = n-2;i >= 0;i--)
        {
            while(m>lim&&((s[m-1]-s[m-2])^(p[i]-s[m-2]))<=0)m--;
            s[++m] = p[i];
        }
        if(m!=1)m--;
        return m;
    }
    建凸包

    5.直线切割凸包(返回直线左边的点)

    比较暴力?

    暴力扫,用叉积判断点是否在直线左边(或直线上)。如果直线与当前线段相交就把交点扔进去。

    Pol Cut_Pol(Pol p,Line l)
    {
        Pol ret;int n = p.size();
        for(int i=0;i<n;i++)
        {
            Point p0 = p[i],p1 = p[(i+1)%n];
            if(dcmp(l.v^(p0-l.x))>=0)ret.push_back(p0);
            if(dcmp(l.v^(p1-p0)))
            {
                Point tmp = L_L(l,Line(p0,p1-p0));
                if(P_S(tmp,p0,p1))ret.push_back(tmp);
            }
        }
        return ret;
    }
    一刀切凸包

    6.半平面交

    首先介绍一下他是干嘛的。

    当然不是我介绍

    用途?比如求一堆多边形重叠部分多大了长什么样子。

    其实还是很暴力的。双端队列大力维护点集+边集,然后……

    (我已经想好了一个绝佳的方式,可惜这里太小写不下)

    主要见代码。

    const int N = 1050;
    Point tmpP[N];
    Line tmpL[N];
    bool Onleft(Line l,Point p)
    {
        return dcmp((l.v^(p-l.x))) > 0;
    }
    int HalfPol(Line *l,int n,Pol &p)
    {
        sort(l,l+n);
        int hd = 0,tl = 0;
        tmpL[0] = l[0];
        for(int i=0;i<n;i++)
        {
            while(hd<tl&&!Onleft(l[i],tmpP[tl-1]))tl--;
            while(hd<tl&&!Onleft(l[i],tmpP[hd]))hd++;
            tmpL[++tl] = l[i];
            if(!dcmp(tmpL[tl].v^tmpL[tl-1].v))
            {
                tl--;
                if(Onleft(tmpL[tl],l[i].x))tmpL[tl]=l[i];
            }
            if(hd<tl)tmpP[tl-1] = L_L(tmpL[tl-1],tmpL[tl]);
        }
        while(hd<tl&&!Onleft(tmpL[hd],tmpP[tl-1]))tl--;
        if(tl-hd<=1)return 0;
        tmpP[tl] = L_L(tmpL[hd],tmpL[tl]);
        for(int i=hd;i<=tl;i++)p.push_back(tmpP[i]);
        return tl-hd+1;
    }
    半平面交

    7.旋转卡壳

    求凸包直径怎么求?

    想象有一对平行的夹子,一段夹在凸包的某条边上,另一端自己转,一直转到一个最舒服的位置就像这样:

    现在夹子之间最长的是两条红线长的$max$。

    代码:

    double rororokk(Pol p)
    {
        int n = p.size();
        int i = 0,j = 1;p[n]=p[0];
        double ans = 0.0;
        for(;i<n;i++)
        {
            while(j<n&&((p[j+1]-p[i+1])^(p[i]-p[i+1]))>((p[j]-p[i+1])^(p[i]-p[i+1])))j=(j+1)%n;
            ans = max(ans, max(lth(p[j]-p[i]),lth(p[j+1]-p[i+1])));
        }
        return ans;
    }
    旋转卡壳
  • 相关阅读:
    阿里云服务器 FTP配置图文教程和添加两个FTP站点
    SAP 用事务码SQVI 做简单报表 .
    公司间联动权限解决方案
    table合并单元格colspan和rowspan .
    js更改input标签的读写属性
    小div在大div中垂直居中,以及div在页面垂直居中
    UI控件(UITextField)
    node基本理念(事件、多线程、进程)
    MyBatis(跨表查询)
    MyBatis(增删改查)
  • 原文地址:https://www.cnblogs.com/LiGuanlin1124/p/10970723.html
Copyright © 2011-2022 走看看