这个题目做了很久,一直wa。后来发现,原来多边形可能不是凸多边形,一下子醒过来了。既然不是凸多边形,那么面积法和叉积法都不能用了,看到网上有说用射线法的,也就把这个题目当做学习射线法的例题了。判断点是否在多边形内部(并非凸都变形),射线法的算法流程大致如下:
由被测点做射线,计算这条射线与多边形边的交点的个数如果是奇数,则说明点再多边形的内部,如果是偶数,说明点再多边形的外部。但是遇到如果射线与多边形的顶点相交的情况,需要太特殊的处理。此时可以用到线段非规范相交的模板,黑书上有这个模板。这个题目是个模板题目,还得回过头来好好看看。这个算法真的很优美。
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
#define N 103
#define eps 1e-10
struct point
{
double x;
double y;
}p[N];
struct beeline
{
point a,b;
};
int n;
bool dy(double x,double y)
{
return x>y+eps;
}
bool xy(double x,double y)
{
return x<y-eps;
}
bool dyd(double x,double y)
{
return x>y-eps;
}
bool xyd(double x,double y)
{
return x<y+eps;
}
bool dd(double x,double y)
{
return fabs(x-y)<eps;
}
double det(double x1,double y1,double x2,double y2)
{
return x1*y2-x2*y1;
}
double cross(point a,point b,point c)
{
return det(c.x-a.x,c.y-a.y,b.x-a.x,b.y-a.y);
}
bool onSegment(point a,point b,point c)//判断点是否在射线上边
{
double maxx=max(a.x,b.x); //找个多边形的x,y的边界值
double maxy=max(a.y,b.y);
double minx=min(a.x,b.x);
double miny=min(a.y,b.y);
//判断点c是否在射线上所在的直线上 如果在 判断点c是否在多边形的区域内,如果成了则点c再射线上
if(dd(cross(a,b,c),0.0)&&dyd(c.x,minx)&&xyd(c.x,maxx)&&dyd(c.y,miny)&&xyd(c.y,maxy))
return true;
return false;
}
bool segIntersect(point p1,point p2,point p3,point p4)
{
double d1=cross(p3,p4,p1);
double d2=cross(p3,p4,p2);
double d3=cross(p1,p2,p3);
double d4=cross(p1,p2,p4);
if(xy(d1*d2,0.0)&&xy(d3*d4,0.0))return true; //如果规范相交
if(dd(d1,0.0)&&onSegment(p3,p4,p1))return true;//如果非规范相交
if(dd(d2,0.0)&&onSegment(p3,p4,p2))return true;
if(dd(d3,0.0)&&onSegment(p1,p2,p3))return true;
if(dd(d4,0.0)&&onSegment(p1,p2,p4))return true;
return false;
}
bool inPolygon(point pot)
{
int count=0;
beeline l;
l.a=pot;
l.b.y=pot.y;
l.b.x=1e10;
p[n]=p[0];
for(int i=0;i<n;i++)
{
if(onSegment(p[i],p[i+1],pot))return true;
if(!dd(p[i].y,p[i+1].y)) //标记不与x轴平行
{
int tmp=-1;
if(onSegment(l.a,l.b,p[i]))
tmp=i;
else if(onSegment(l.a,l.b,p[i+1]))
tmp=i+1;
if(tmp!=-1&&dd(p[tmp].y,max(p[i].y,p[i+1].y)))
count++;
else if(tmp==-1&&segIntersect(p[i],p[i+1],l.a,l.b))
count++;
}
}
if(count%2==1)return true;
else return false;
}
int main()
{
int m;
int cn=1;
point pot;
while(scanf("%d",&n)&&n)
{
scanf("%d",&m);
if(cn!=1)printf("\n");
for(int i=0;i<n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
}
printf("Problem %d:\n",cn++);
while(m--)
{
scanf("%lf%lf",&pot.x,&pot.y);
if(inPolygon(pot))printf("Within\n");
else printf("Outside\n");
}
}
return 0;
}