题意 : 给你一个多边形,问你在多边形内部是否存在这样的点,使得这个点能够看到任何在多边形边界上的点。
思路 : 半平面交求多边形内核。
关于求多边形内核的算法
什么是多边形的内核?
它是平面简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形内部。就是一个在一个房子里面放一个摄像 头,能将所有的地方监视到的放摄像头的地点的集合即为多边形的核。
如上图,第一个图是有内核的,比如那个黑点,而第二个图就不存在内核了,无论点在哪里,总有地区是看不到的。
那么,如何求得这个内核区间呢?通常的算法是用两点的直线去不断切割多边形,切割到最后剩下的,就是内核区间了。
我们都知道一条直线可以将平面切割成两个区域,假设直线方程为
ax+by+c==0,那么,两个平面可分别表示成ax+by+c>=0 和 ax+by+c<0
具体如何用程序实现直线对多边形的切割呢?
流程是这样的:
1、 用一个顺时针或者逆时针的顺序,将最初的多边形的点集储存起来。
2、 按顺序取连续的两个点组成一条直线,用这条直线来切割原先的多边形
我首先假设点是顺时针储存的,如图:
此时,多边形的点集是{1,2,3,4,5,6,7,8,9,10}
取点1,和点2组成直线ax+by+c==0,这时候,将点集中的点一次带入方程ax+by+c,得到的值都将会是大于等于0的,说明所有的点都在该直线的同一侧,继续保持点集不变
取点2和点3组成直线,同样,将点集中的点依次带入方程ax+by+c中,此时,4和5两个点的结果是小于0的,而其他的点的值依旧是大于等于0,这时候说明4和5两个点被切割出了该多边形,于是现在点集只剩下{1,2,3,6,7,8,9,10,X},(X是直线23和直线56的交点)
依次类推,一直执行到点10和点1,那么内核的集合就得到了。
值得说明的是,这个例子的图形比较特殊,全是直角,如果图形比较随意,那么当某一个点被断定在多边形区间之外的时候,我们还应该考虑它和它相邻的两个点各自组成的直线和ax+by+c有没有交点,有交点的话,更新的点集中还应该加上这些交点,比如例子中执行完点2和点3组成的直线后,点集是{1,2,3,6,7,8,X},其中3和X就是这样的结果
还有,为什么将所有的点依次执行一遍,然后取剩下的某一边的点构成新的点集就够了呢?答案是,点是顺时针或者逆时针给出的~~~
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <math.h> 5 6 using namespace std ; 7 8 struct node 9 { 10 double x; 11 double y ; 12 } p[110],temp[110],newp[110];//p是最开始的多边形的每个点,temp是中间过程中临时存的多边形的每个点,newp是切割后的多边形的每个点 13 int n,newn ;//原来的点数,切割后的点数 14 double a,b,c ;//直线方程的三个系数 15 16 void getline(node x,node y)//求x与y两点确定的直线方程ax+by+c=0 17 { 18 a = y.y-x.y ; 19 b = x.x-y.x ; 20 c = y.x*x.y - y.y*x.x ; 21 } 22 node intersect(node x,node y)//求x与y点确定的直线与ax+by+c=0这条直线的交点 23 { 24 double u = a*x.x+b*x.y+c ; 25 double v = a*y.x+b*y.y+c ; 26 node t ; 27 t.x = (x.x*v+y.x*u)/(u+v) ;//y.y-x.y=u+v;y.y-t.y=v;y.y-x.y=u; 28 t.y = (x.y*v+y.y*u)/(u+v) ; 29 return t ; 30 } 31 void cut() 32 { 33 int cutn = 0 ; 34 for(int i = 1 ; i <= newn ; i++) 35 { 36 if(a*newp[i].x+b*newp[i].y+c >= 0)//所有的点都大于0,说明所有的点都在这条直线的另一边,所以不用切 37 temp[ ++cutn] = newp[i] ; 38 else 39 { 40 if(a*newp[i-1].x+b*newp[i-1].y+c > 0) 41 temp[++cutn ] = intersect(newp[i-1],newp[i]) ;//把新交点加入 42 if(a*newp[i+1].x+b*newp[i+1].y+c > 0) 43 temp[ ++cutn] = intersect(newp[i+1],newp[i]) ; 44 } 45 } 46 for(int i = 1 ; i <= cutn ; i++) 47 newp[i] = temp[i] ; 48 newp[cutn+1] = temp[1] ;//能够找出所有点的前驱和后继 49 newp[0] = temp[cutn] ; 50 newn = cutn ; 51 } 52 53 void solve() 54 { 55 for(int i = 1 ; i <= n ; i++) 56 { 57 newp[i] = p[i] ; 58 } 59 p[n+1] = p[1] ; 60 newp[n+1] = newp[1] ; 61 newp[0] = newp[n] ; 62 newn = n ; 63 for(int i = 1 ; i <= n ; i++) 64 { 65 getline(p[i],p[i+1]) ;//从头开始顺序遍历两个相邻点。 66 cut() ; 67 } 68 69 } 70 int main() 71 { 72 int T ; 73 scanf("%d",&T) ; 74 while(T--) 75 { 76 scanf("%d",&n) ; 77 for(int i = 1 ; i <= n ; i++) 78 scanf("%lf %lf",&p[i].x,&p[i].y) ; 79 solve() ; 80 if(newn == 0) puts("NO") ; 81 else puts("YES") ; 82 } 83 return 0; 84 }