分析
射线法
虽然这个射线法有很多需要特判的情况,但总的来说还是蛮好用的
判断点与多边形的关系,若使用射线法,就是说从这个点往右做一条与 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;
}