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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值