Cupid's Arrow
Problem Description
传说世上有一支丘比特的箭,凡是被这支箭射到的人,就会深深的爱上射箭的人。
世上无数人都曾经梦想得到这支箭。Lele当然也不例外。不过他想,在得到这支箭前,他总得先学会射箭。
日子一天天地过,Lele的箭术也越来越强,渐渐得,他不再满足于去射那圆形的靶子,他开始设计各种各样多边形的靶子。
不过,这样又出现了新的问题,由于长时间地练习射箭,Lele的视力已经高度近视,他现在甚至无法判断他的箭射到了靶子没有。所以他现在只能求助于聪明的Acmers,你能帮帮他嘛?
世上无数人都曾经梦想得到这支箭。Lele当然也不例外。不过他想,在得到这支箭前,他总得先学会射箭。
日子一天天地过,Lele的箭术也越来越强,渐渐得,他不再满足于去射那圆形的靶子,他开始设计各种各样多边形的靶子。
不过,这样又出现了新的问题,由于长时间地练习射箭,Lele的视力已经高度近视,他现在甚至无法判断他的箭射到了靶子没有。所以他现在只能求助于聪明的Acmers,你能帮帮他嘛?
Input
本题目包含多组测试,请处理到文件结束。
在每组测试的第一行,包含一个正整数N(2<N<100),表示靶子的顶点数。
接着N行按顺时针方向给出这N个顶点的x和y坐标(0<x,y<1000)。
然后有一个正整数M,表示Lele射的箭的数目。
接下来M行分别给出Lele射的这些箭的X,Y坐标(0<X,Y<1000)。
在每组测试的第一行,包含一个正整数N(2<N<100),表示靶子的顶点数。
接着N行按顺时针方向给出这N个顶点的x和y坐标(0<x,y<1000)。
然后有一个正整数M,表示Lele射的箭的数目。
接下来M行分别给出Lele射的这些箭的X,Y坐标(0<X,Y<1000)。
Output
对于每枝箭,如果Lele射中了靶子,就在一行里面输出"Yes",否则输出"No"。
Sample Input
4 10 10 20 10 20 5 10 5 2 15 8 25 8
Sample Output
Yes No
Author
linle
Source
判断点是否在多边形内
采用射线法
对于给定点引一条射线
判射线与多边形的交点个数
若交点为奇数(射线从多边形内部出去)
则点在多边形内
若交点为偶数(射线从多边形外穿过多边形出去)
则点在多边形外
但是 有一些奇怪的情况
![]()
图(1) 图(2)
![]()
图(3) 图(4)
部分人选择再随机引一条射线
但如果遇到比较坑的多边形
或者自带非洲人光环
你就GG了
我们需要加一些特殊判断
考虑这些奇怪的情况
对于点在边上的情况 我们可以直接判断
对于与射线平行的边 我们跳过它
考虑交到交点上的情况
图(2)中,我们应将交点算1个点
图(4)中,我们应将交点算2个点
现在对于一条边 考虑忽略该线段上最下面的一个点
恰好可以满足所有的情况(可以思考一下为什么)
这样我们就得到了一个靠谱的方法
#include #include #include using namespace std; const int MaxN = 233; const double MaxX = 6666; const double eps = 1e-7; int n, m; int sgn(double x) { if (fabs(x) <= eps) return 0; return x < 0 ? -1 : 1; } struct point { double x, y; point () {} point (double a, double b) { x = a, y = b; } point operator + (const point &b) const { return point(x + b.x, y + b.y); } point operator - (const point &b) const { return point(x - b.x, y - b.y); } point operator * (const double &b) const { return point(x * b, y * b); } point operator / (const double &b) const { return point(x / b, y / b); } double operator * (const point &b) const { return x * b.y - b.x * y; } }P[MaxN], Q, Qr; struct seg { point a, b; seg () {} seg (const point &x, const point &y) { a = x, b = y; } bool on(const point &c) { double Minx = min(a.x, b.x), Maxx = a.x + b.x - Minx; if (c.x < Minx || c.x > Maxx) return false; double Miny = min(a.y, b.y), Maxy = a.y + b.y - Miny; if (c.y < Miny || c.y > Maxy) return false; return sgn((c - a) * (b - a)) == 0; } }Qs; bool cross(const seg &a, const seg &b) { double l, r, L, R; l = min(a.a.x, a.b.x), r = a.a.x + a.b.x - l; L = min(b.a.x, b.b.x), R = b.a.x + b.b.x - L; if (r < L || R < l) return false; l = min(a.a.y, a.b.y), r = a.a.y + a.b.y - l; L = min(b.a.y, b.b.y), R = b.a.y + b.b.y - L; if (r < L || R < l) return false; if (sgn(((a.b - a.a) * (b.a - a.a)) * ((a.b - a.a) * (b.b - a.a))) > 0) return false; if (sgn(((b.b - b.a) * (a.a - b.a)) * ((b.b - b.a) * (a.b - b.a))) > 0) return false; return true; } bool check() { int cnt = 0; for (int i = 0; i < n; ++i) { int j = (i + 1) % n; seg S = seg(P[i], P[j]); if (S.on(Q)) return false; if (P[i].y == P[j].y) continue; if (!cross(S, Qs)) continue; double y = min(P[i].y, P[j].y); if (y == Q.y) continue; ++cnt; } return cnt & 1; } int main() { while (scanf("%d", &n) == 1) { for (int i = 0; i < n; ++i) scanf("%lf%lf", &P[i].x, &P[i].y); scanf("%d", &m); for (int i = 1; i <= m; ++i) { scanf("%lf%lf", &Q.x, &Q.y); Qr = point(-MaxX, Q.y); Qs = seg(Q, Qr); if (check()) printf("Yes\n"); else printf("No\n"); } } }
另注:
如果判断点在凸多边形内
可以直接用判断点在多边形内的做法
但基于凸多边形的特殊性
我们还有另一个简便一些的做法
若一个点在凸多边形内
那么 以这个点和凸多边形的边组成的三角形的有向面积是同号的
否则 以这个点和凸多边形的边组成的三角形的有向面积是异号的
基于这一点 我们可以算出每个小三角形的面积
若小三角形的面积和即为凸多边形的面积
那么这个点在凸多边形内
——by Abzoey