zoukankan      html  css  js  c++  java
  • 计算几何--二维几何常用算法(多边形和凸包)

    内容参考书籍——《算法竞赛入门经典训练指南》  

      在程序中,用顶点数组表示多边形,其中各个顶点按照逆时针顺序排列。

      判断点是否在多边形内。采用转角法,基本思想是计算多边形相对于判定点转了多少度,具体来说,将多边形每条边的转角加起来,如果是360°,说明在多边形内;如果是0°,说明在多边形如果是180°则在多边形边界上。该方法在处理一些弧形多边形时丝毫不受影响,只需要每一段的终点到起点的转角累加起来即可。另外这个三角形甚至可以不是简单多边形(即可以自交)。

      然而,直接计算会使用大量的反三角函数,不仅速度慢且容易产生精度误差。在算法竞赛中,我们并不会这样做,而是假想有一条向右的射线,统计多边形穿过这条射线正反多少次,把这个数记为绕数wn(Winding Number),逆时针穿过时,wn加1,顺时针穿过时,wn减1。

      注意程序实现时,判断是否穿过,以及穿过方向时,需要用叉积判断输入点在边的左边还是右边。

      点在凸多边形内的判定更简单,只需要判断是否在所有边的左边(假设各顶点按照逆时针顺序排序)即可

      凸包。

      凸包就是把定点包围在内部的、面积最小的凸多边形。基于水平序的Andrew算法(比原始的Graham更快且更稳定)。首先把所有点按x从小到大排序(如果x相同,按照y从小到大排序),删除重复点后得到序列p1,p2,...,然后把p1和p2放到凸包中。从p3开始,当新点在凸包“前进”方向的左边时继续,否则依次删除最近加入凸包的点,直到新点在左边。

      如下图所示,新点P18在向量P10P15(当前“前进方向“)的右边,因此需要从凸包上删除P15和P10,让P8的下一个点为P18。重复这个过程,直到碰到最右边的Pn,就求出了“下凸包”。然后反过来从Pn开始再做一次,求出“上凸包”,合并起来就是完整的凸包。

      这个算法在排序后仅仅是从左到右和从右到左各扫描了一次,时间复杂度为O(n)。加上排序后时间复杂度也仅为O(nlogn)。

    代码如下:

     1 //判断该点与多边形关系
     2 int isPointInPolygon(Point p, Polygon poly)
     3 {
     4     int wn = 0;
     5     int n = v.size();
     6     for (int i = 0; i < n; ++i)
     7     {
     8         if (isPointOnSegment(p,poly[i], poly[(i+1)%n])) return -1;
     9         int k = dcmp(Cross(poly[(i+1)%n]-poly[i]),p-poly[i]);
    10         int d1 = dcmp(poly[i].y-p.y);
    11         int d2 = dcmp(poly[(i+1)%n].y-p.y);
    12         if (k>0 && d1<=0 && d2>0) wn++;
    13         if (k<0 && d2<=0 && d1>0) wn--;        
    14     }
    15     if (wn != 0)return 1;
    16     return 0;
    17 }
    18 //计算凸包,输入点数组p,个数为p,输出点数组ch。函数返回凸包顶点数。
    19 //输入不能有重复点。函数执行完之后输入点的顺序被破坏。
    20 //如果不希望在凸包的边上有输入点,把两个<=改成<
    21 //在精度要求高时建议用dcmp比较
    22 int ConvexHull(Point* p, int n, Point* ch)
    23 {
    24     sort(p,p+n);
    25     int m = 0;
    26     for (int i = 0; i < n; ++i)
    27     {
    28         while(m>1 && Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]) <= 0) m--;
    29         ch[m++] = p[i];
    30     }
    31     int k = m;
    32     for (int i = n-2; i >= 0; --i)
    33     {
    34         while(m >k && Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]) <= 0) m--;
    35         ch[m++] = p[i];
    36     }
    37     if (n > 1) m--;
    38     return m;
    39 }

     求多边形的重心

    hdu 1115:给一个n多边形,求重心。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 struct Point
     4 {
     5     double x,y;
     6     Point(double X = 0,double Y = 0){x=X,y=Y;}
     7     Point operator + (Point B) {return Point (x+B.x,y+B.y);}
     8     Point operator - (Point B) {return Point (x-B.x,y-B.y);}
     9     Point operator * (double k) {return Point (x*k,y*k);}
    10     Point operator / (double k) {return Point (x/k,y/k);}
    11 };
    12 typedef Point Vector;
    13 double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}
    14 double Polygon_area(Point *p,int n)     //求多边形有向面积,有正负不能取绝对值
    15 {
    16     double area = 0;
    17     for (int i = 0; i < n; ++i)
    18     {
    19         area += Cross(p[i],p[(i+1)%n]);
    20     }
    21     return area/2;
    22 }
    23 Point Polygon_center(Point *p,int n) //求重心
    24 {
    25     Point ans(0,0);
    26     if (Polygon_area(p,n) == 0) return ans;
    27     for (int i = 0; i < n; ++i)
    28     {
    29         ans = ans+(p[i]+p[(i+1)%n])*Cross(p[i],p[(i+1)%n]);
    30     }
    31     return ans/Polygon_area(p,n)/6;
    32 }
    33 int main(int argc, char const *argv[])
    34 {
    35     int t,n,i;
    36     Point center;
    37     Point p[100000];
    38     scanf("%d",&t);
    39     while(t--)
    40     {
    41         scanf("%d",&n);
    42         for (int i = 0; i < n; ++i)
    43         {
    44             scanf("%lf %lf",&p[i].x,&p[i].y);
    45         }
    46         center = Polygon_center(p,n);
    47         printf("%.2f %.2f
    ",center.x,center.y);
    48     }
    49     return 0;
    50 }

    hdu 1392:输入n个点,求凸包的周长。

    代码如下:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int maxn = 104;
     4 const double eps = 1e-8;
     5 int sgn(double x)         //判断x是否为0
     6 {
     7     if (fabs(x)<eps) return 0;
     8     else return x<0? -1:1;
     9 }
    10 struct Point
    11 {
    12     double x,y;
    13     Point(){}
    14     Point(double x, double y):x(x),y(y){}
    15     Point operator + (Point B) {return Point (x+B.x,y+B.y);}
    16     Point operator - (Point B) {return Point (x-B.x,y-B.y);}
    17     bool operator == (Point B) {return sgn(x-B.x) == 0 && sgn(y-B.y) == 0;}
    18     bool operator < (Point B)    //用于sort()排序
    19     {
    20         return sgn(x-B.x)<0 || (sgn(x-B.x) == 0 && sgn(y-B.y)<0);
    21     }
    22 };
    23 typedef Point Vector;
    24 double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}//叉积
    25 double Distance(Point A, Point B){return hypot(A.x-B.x,A.y-B.y);}
    26 //求凸包。凸包顶点放在ch中,返回值是凸包的顶点数
    27 int ConvexHull(Point* p, int n, Point* ch)
    28 {
    29     sort(p,p+n);              //对点排序:按从大到小排序,如果x相同,按y排序
    30     n=unique(p,p+n)-p;        //去重复点
    31     int m = 0;
    32     //求下凸包,如果p[i]是右拐弯的,这个点不在凸包上,往回退
    33     for (int i = 0; i < n; ++i)
    34     {
    35         while(m>1 && Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]) <= 0) m--;
    36         ch[m++] = p[i];
    37     }
    38     int k = m;                //求上凸包
    39     for (int i = n-2; i >= 0; --i)
    40     {
    41         while(m >k && Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2]) <= 0) m--;
    42         ch[m++] = p[i];
    43     }
    44     if (n > 1) m--;
    45     return m;                //返回凸包顶点数
    46 }
    47 int main(int argc, char const *argv[])
    48 {
    49     int n;
    50     Point p[maxn],ch[maxn];
    51     while(scanf("%d",&n) && n)
    52     {
    53         for (int i = 0; i < n; ++i)
    54         {
    55             scanf("%lf%lf",&p[i].x,&p[i].y);
    56         }
    57         int v = ConvexHull(p,n,ch);//凸包顶点数
    58         double ans = 0;
    59         if (v==1) ans=0;
    60         else if(v==2)ans=Distance(ch[0],ch[1]);
    61         else
    62         {
    63             for (int i = 0; i < v; ++i) //计算凸包周长
    64             {
    65                 ans+=Distance(ch[i],ch[(i+1)%v]);
    66             }
    67         }
    68         printf("%.2f
    ",ans);
    69     }    
    70     return 0;
    71 }

     

  • 相关阅读:
    docker学习网址
    ubantu安装docker
    ssh查看自己的公钥
    Ubuntu 桥接设置 (在ubantu 18.04.5 之前好用 ,,,,18.04.5 就不好用了)
    学习地址
    MANO官网
    centos7 的防火墙 开放端口
    centos7安装arp-sacn (./configure make makeinstall)
    yum源-postgresql
    行拆分,列合并
  • 原文地址:https://www.cnblogs.com/125418a/p/11588262.html
Copyright © 2011-2022 走看看