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

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

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

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

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

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

  凸包。

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

(待补充说明)

代码如下:

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