给一个 (n) 个点的多边形,给一个点 (P),问点 (P) 是否在多边形内(或多边形上)。
我们随便画一个多边形,随便在多边形内点一点,随便向外发一条射线,可以发现这条射线与多边形的交点个数为奇数。然后就做完了。
对这条性质的简单证明:我们发现多边形内一点想要到外面,必须经过一条边,如果再进去,同样必须经过一条边,以此类推,可以得到结论。
还是有很多特殊情况的。例如射线经过多边形一个顶点的情况,或者这个点 (P) 本身就在多边形边上的情况等等。
所以我们的具体流程是这样的:
- 判断点 (P) 在不在多边形的边上,如果不在进行下一步
- 随机一个点 (Q),若点 (PQ) 与多边形的交点中有多边形的顶点,则重新随 (Q),直到满足条件
- 然后判断 (PQ) 与多边形有多少交点就好啦
时间复杂度:(O(n))。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-10;
const int N=100009;
int n;
struct Point
{
double x,y;
Point () {}
Point (double X,double Y) : x(X),y(Y) {}
Point operator - (const Point a)const { return Point(x-a.x,y-a.y); }
double operator * (const Point a)const { return x*a.y-y*a.x; }
void read() { scanf("%lf %lf",&x,&y); }
void print() { printf("%.2lf %.2lf
",x,y); }
}Q,P,a[N];
void init()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
a[i].read();
P.read();
}
int nxt(int x) { return x==n?1:x+1; }
bool Point_In_Edge(Point A,Point B,Point C)
{
double Minx=min(B.x,C.x),Maxx=max(B.x,C.x),Miny=min(B.y,C.y),Maxy=max(B.y,C.y);
if(fabs((C-B)*(A-B))<eps&&A.x+eps>Minx&&A.x<Maxx+eps&&A.y+eps>Miny&&A.y<Maxy+eps)
return 1;
return 0;
}
bool Both_Point(Point P1,Point P2,Point P3,Point P4)
{
if(!(max(P1.x,P2.x)+eps>=min(P3.x,P4.x)&&min(P1.x,P2.x)<=max(P3.x,P4.x)+eps))
return 0;
if(!(max(P1.y,P2.y)+eps>=min(P3.y,P4.y)&&min(P1.y,P2.y)<=max(P3.y,P4.y)+eps))
return 0;
if(((P2-P1)*(P3-P1))*((P2-P1)*(P4-P1))>eps)
return 0;
if(((P4-P3)*(P1-P3))*((P4-P3)*(P2-P3))>eps)
return 0;
return 1;
}
void work()
{
for (int i=1;i<=n;i++)
if(Point_In_Edge(P,a[i],a[nxt(i)]))
{
puts("Edge");
return;
}
int qwq=1;
while(qwq)
{
qwq=0;
Q.x=(rand()<<15)+rand()+10000000;
Q.y=(rand()<<15)+rand()+10000000;
for (int i=1;i<=n;i++)
if(Point_In_Edge(a[i],P,Q))
{
qwq=1;
break;
}
if(qwq) continue;
int sum=0;
for (int i=1;i<=n;i++)
sum+=Both_Point(a[i],a[nxt(i)],P,Q);
if(sum&1) puts("in");
else puts("out");
}
}
int main()
{
init();
work();
return 0;
}