凸多邊形
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