2018.10.15【ZOJ1081】Points Within(角度判别法)

本文介绍了一种用于判断点是否位于多边形内部的算法,通过计算角度和利用点积转换来实现,避免了传统射线法可能带来的精度误差。文中详细解释了算法的原理,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


解析:

一种比较通用的方法叫射线法

而角度判别法因为可能的精度误差以及计算三角函数所用的大常数,被许多人所抛弃。。。。

思路:

其实角度判别法的常数并没有那么的骇人听闻。

只需要计算一个 a c o s acos acos和两个 s q r t sqrt sqrt

主要方法就是计算出每个边两端点与询问点连线所夹角度之和(考虑负角)。

而计算角度所用的主要方法,就是从点积转化。

具体就是这样 r a d = a c o s ( a ∙ b ∣ a ∣ × ∣ b ∣ ) rad=acos(\frac{a\bullet b}{|a|\times|b|}) rad=acos(a×bab)

更多细节直接看代码


代码(很多不需要加 f a b s fabs fabs的地方加了,懒得改了):

#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;}
	double norm()cs{return sqrt(dot(*this,*this));}
	friend double angle(const Point &a,const Point &b){return acos(dot(a,b)/a.norm()/b.norm());}
	
};

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

cs double PI=acos(-1);
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{
		double rad=0;
		for(int re i=1;i<=n;++i){
			if(onseg(p[i],p[i%n+1],s))return true;
			double Ang=fabs(angle(p[i]-s,p[i%n+1]-s));
			if((p[i]-s)*(p[i%n+1]-s)>0)rad+=Ang;
			else rad-=Ang;
		}
		return fabs(fabs(rad)-PI*2)<1e-6;
	}
}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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值