1. 程式人生 > >1015 - 計算幾何之點與多邊形的關係 - Points Within(ZOJ1081)

1015 - 計算幾何之點與多邊形的關係 - Points Within(ZOJ1081)

傳送門

 

分析

射線法

雖然這個射線法有很多需要特判的情況,但總的來說還是蠻好用的

判斷點與多邊形的關係,若使用射線法,就是說從這個點往右做一條與 x 軸平行的射線,看它與多邊形相交了幾次

若相交了偶數次,則不在多邊形內部(相當於從這個多邊形中穿過去,沒有留在裡面)

若相交了奇數次,則在多邊形內部

1.要特殊考慮一下這種情況

若剛好穿過一個點,照我們的方法,會被計算兩次(上面的邊一次,下面的邊一次),而這明顯不對

所以我們就要強制規定,經過一條邊的左端點(或右端點)才能被計算

2.如果這個點剛好落在多邊形的邊上,(由於這道題我們是把這種情況當做落在裡面)我們需要判斷一下

3.由於射線法是往一個方向延伸,所以當一條邊一條邊的加入,邊轉到這個點的左邊(或右邊)時,就不能納入計數了

在程式碼中實現就是這樣的:
 

int d1=a[i].y-b.y;int d2=a[j].y-b.y;
if((det>=0&&d1<0&&d2>=0)||(det<=0&&d1>=0&&d2<0)) cnt++;

d1記錄 i 這個頂點與 b 縱座標之差,d2記錄下一個 i+1 這個頂點與 b 的縱座標之差。

 

程式碼

 

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-7;
struct point{
	int x,y;
	point (int _x=0.0,int _y=0.0):x(_x),y(_y){} 
	friend inline point operator +(const point &a,const point &b){
		return point(a.x+b.x,a.y+b.y);
	}
	friend inline point operator -(const point &a,const point &b){
		return point(a.x-b.x,a.y-b.y);
	}
	friend inline point operator *(double k,const point &a){
		return point(k*a.x,k*a.y);
	}
	friend inline int dot(const point &a,const point &b){
		return (a.x*b.x+a.y*b.y);
	}
	friend inline int cross(const point &a,const point &b){
		return (a.x*b.y-b.x*a.y);
	}
	friend inline double len(const point &a){
		return sqrt(dot(a,a));
	}
	friend inline double dis (const point &a,const point &b){
		return len(a-b);
	}//向量常見的運算 
}a[105],b;
int n,m;
bool check(){
	int cnt=0;
	for(int i=1;i<=n;++i){
		int j=i+1;
		if(j>n) j=1;
		int det=cross(a[i]-b,a[j]-b);
		if(det==0)	if(dot(a[i]-b,a[j]-b)<=0) return 1;
		int d1=a[i].y-b.y;int d2=a[j].y-b.y;
		if((det>=0&&d1<0&&d2>=0)||(det<=0&&d1>=0&&d2<0)) cnt++;
	}
	return cnt&1;
}
int main(){
	for(int tt=1;;tt++){
		scanf("%d",&n);
		if(n==0) break;
		scanf("%d",&m);
		int i,j,k;
		for(i=1;i<=n;++i)
			scanf("%d%d",&a[i].x,&a[i].y);
		if(tt!=1) printf("\n");
		printf("Problem %d:\n",tt);	
		for(i=1;i<=m;++i){
			scanf("%d%d",&b.x,&b.y);
			if(check()) printf("Within\n");
			else printf("Outside\n");
		}
	} 
	return 0;
}