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、选择多边形其中一个点为起点,连接其它点作射线。

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

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

  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

posted @ 2014-04-18 16:52  Freecode#  阅读(4410)  评论(0编辑  收藏  举报