hrbustoj 1429:凸多边形(计算几何,判断点是否在多边形内,二分法)

凸多边形

Time Limit: 2000 MS    Memory Limit: 65536 K

Total Submit: 130(24 users)   Total Accepted: 40(18 users)       Rating:         Special Judge: No

Description

已知一个凸多边形A(包含n个点,点按照顺时针给出),和一个点集B(包含m个点),请判断这m个点是否都严格在凸多边形A内部。

 

Input

输入包含多组测试数据。

对于每组测试数据:

第1行,包含一个整数n (3 ≤ n ≤ 105)代表着凸多边形A的点的数量。

接下来n行每行包含一个坐标(x, y) (-109 ≤ x, y ≤ 109) 表示这个凸多边形,点按照顺时针给出。

第n + 2行,包含一个整数m (3 ≤ m ≤ 105)代表着点集B的点的数量。

接下来m行每行包含一个坐标(x, y) (-109 ≤ x, y ≤ 109) 表示这个点集B。

处理到文件结束

 

Output

对于每组测试数据:

第1行,如果点集B都严格在凸多边形A内,输出YES,否则输出NO。

 

Sample Input

4

-10 -10

-10 10

10 10

10 -10

3

0 0

1 1

2 2

4

-10 -10

-10 10

10 10

10 -10

3

100 100

1 1

2 2

 

Sample Output

YES

NO

 

Author

齐达拉图@HRBUST


 

  计算几何,判断点是否在多边形内(二分法)

  判断点是否在多边形内有多种方法,例如:射线法,角度和判断法,弧长法,二分法。

  射线法是我最开始学的方法,较麻烦;角度和判断法也叫转角法,比较方便,但是由于要计算大量的反三角函数,所以速度较慢,容易产生精度误差。而弧长法的优点恰恰就是精度高,只需作乘法和减法,若对整数坐标则完全没有精度问题。而且实现简单,比射线法和转角法都好写。二分法速度最快,特别适应于判断多个点是否在多边形内的情况。就像这道题。

  其中前三种方法时间复杂度都是O(n),二分法时间复杂度是O(logn)

  这道题的题意是已知构成凸多边形A的n个点的坐标,和点集B的m个点的坐标,求这B的m个点是否都在凸多边形A内(严格内部,就是点不能在多边形边上)。

  思路:用以上前三种方法的任意一种都会超时,时间复杂度为(O(mn)),遂使用二分法,这道题的时间复杂度为(O(mlogn))。

  二分法求多边形的步骤:

  1、选择多边形其中一个点为起点,连接其它点作射线。

hrbustoj 1429:凸多边形(计算几何,判断点是否在多边形内,二分法)

  2、判断给定的点是否在所有射线包围的区域之内,即判断给定点是否在最左侧射线的左边,或者在最右侧射线的右边。

  3、如果在射线包围的区域之内,选择构成最两侧的射线的点为left和right,则mid = (left+right)/2,连接给顶点和起点作射线,判断该射线在mid点和起点的哪一边,不断循环,如此用二分法最后求出给定点所在的三角形区域,由此确定了除起点外的一条边。

hrbustoj 1429:凸多边形(计算几何,判断点是否在多边形内,二分法)

  4、判断给定点在这条边的左方还是右方,由此判断给定点是否在三角形区域内,也就是是否在多边形内。

  注意:这道题有个坑,点要求严格在多边形内部,也就是说不能在多边形的边上。注意这一点,测试数据控制的很严格,WA了好多次才明白过来。

  代码

 1 #include <stdio.h>

 2 #define eps 1e-10

 3 struct Point{  4     double x,y;  5 };  6 double xmulti(Point p1,Point p2,Point p0)    //求p1p0和p2p0的叉积,如果大于0,则p1在p2的顺时针方向

 7 {  8     return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);  9 } 10 Point A[100001],B[100001]; 11 int main() 12 { 13     int i,n,m; 14     while(scanf("%d",&n)!=EOF){ 15         for(i=1;i<=n;i++)    //输入多边形的顶点

16             scanf("%lf%lf",&A[i].x,&A[i].y); 17         scanf("%d",&m); 18         for(i=1;i<=m;i++)    //输入点集

19             scanf("%lf%lf",&B[i].x,&B[i].y); 20         //二分法判断B上的点是否在原凸多边形A内,注意在边上不行

21         for(i=1;i<=m;i++){    //B[i]

22             if(xmulti(B[i],A[2],A[1])<=eps || xmulti(B[i],A[n],A[1])>=-eps)    //在第一个点为起点的扇形之外或在边上

23                 break; 24             int left=2,right=n; 25             while(right-left!=1){ 26                 int mid = (left+right)/2; 27                 if(xmulti(B[i],A[mid],A[1])>eps) 28                     left = mid; 29                 else 

30                     right = mid; 31  } 32             if(xmulti(B[i],A[right],A[left])<=eps)    //在边之外或在边上

33                 break; 34  } 35         if(i>m) 36             printf("YES\n"); 37         else

38             printf("NO\n"); 39  } 40     return 0; 41 }

 

Freecode : www.cnblogs.com/yym2013

你可能感兴趣的:(二分法)