HDU1756:Cupid's Arrow

Cupid's Arrow

Problem Description
传说世上有一支丘比特的箭,凡是被这支箭射到的人,就会深深的爱上射箭的人。
世上无数人都曾经梦想得到这支箭。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)。
 

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值