2018.10.15【ZOJ1081】Points Within(射线法)

传送门


解析:

射线法裸题,其实也可以用角度判别法过去。

思路:

判断一个点是否在多边形内部,我们从这个点向任意方向随便引一条射线,如果这条射线与多边形的交点有奇数个,那么这个点就是在多边形内部的。

简单口胡 证明:
考虑一条直线穿过一个多边形,它一定与多边形有偶数个交点,那么我们从第一个交点向后走,显然这段是在多边形内部的,然后经过一个交点后我们会在这个多边形外部。
因为多边形的边就是他内部和外部的分界线,自然这种性质就体现在了交点上。
那么当经过奇数个交点的时候,在多边形内部。

但是显然会有很多特殊情况。

比如射线交在了多边形的顶点上。。。
射线与多边形的边重合。。。等等例子

其实边重合很好理解,直接特判点是否在边上,如果在,根据题目要求直接返回答案(在多边形内是否包含多边形的边)。如果不在,那我们无视这条边与射线的交点,不会影响答案。

如果是顶点重合,还要分情况,如果与重合顶点相邻的两边在射线同侧,显然穿过这个顶点之后仍然在多边形内,这个顶点就不能计算入交点。

反之,则需要计算,为保证只计算一次,我们规定与边的某一个端点交才算交,与另一个端点交则不算。

虽然讨论的情况很多,但是最终化归出的判断方法非常简单,具体请看代码中结构体 P o l y g o n Polygon Polygon的成员函数 i n n e r inner inner


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	re bool f=0;
	while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return f?-num:num;
}

struct Point{
	int x,y;
	Point(cs int &_x=0,cs int &_y=0):x(_x),y(_y){}
	
	friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
	friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
	friend int operator*(cs Point &a,cs Point &b){return a.x*b.y-a.y*b.x;}
	friend int dot(cs Point &a,cs Point &b){return a.x*b.x+a.y*b.y;}
};

inline bool onseg(cs Point &u,cs Point &v,cs Point &p){
	if((u-p)*(v-p)!=0)return false;
	return dot(u-p,v-p)<=0;
}

struct Polygon{
	int n;
	Point p[102];
	
	void get(){for(int re i=1;i<=n;++i)p[i].x=getint(),p[i].y=getint();}
	
	void clear(){memset(p,0,sizeof p);}
	
	bool inner(cs Point &s)cs{
		int cnt=0;
		for(int re i=1;i<=n;++i){
			if(onseg(p[i],p[i%n+1],s))return true;
			int d1=p[i].y-s.y,d2=p[i%n+1].y-s.y;
			int det=(p[i]-s)*(p[i%n+1]-s);
			if((det>=0&&d1<0&&d2>=0)||(det<=0&&d1>=0&&d2<0))++cnt;
		}
		return cnt&1;
	}
}P;

int m;
signed main(){
	for(int re tt=1;;++tt){
		P.n=getint();
		if(P.n==0)return 0;
		m=getint();
		P.clear();
		P.get();
		if(tt!=1)pc('\n');
		printf("Problem %d:\n",tt);
		while(m--){
			Point s;
			s.x=getint();
			s.y=getint();
			if(P.inner(s))puts("Within");
			else puts("Outside");
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值