题目:http://lightoj.com/volume_showproblem.php?problem=1190

参考链接:https://blog.csdn.net/gkingzheng/article/details/81836308

http://www.mamicode.com/info-detail-1555428.html

一、比如说,我就随便涂了一个多边形和一个点,现在我要给出一种通用的方法来判断这个点是不是在多边形内部(别告诉我用肉眼观察……)。

首先想到的一个解法是从这个点做一条射线,计算它跟多边形边界的交点个数,如果交点个数为奇数,那么点在多边形内部,否则点在多边形外。

这个结论很简单,那它是怎么来的?下面就简单讲解一下。

首先,对于平面内任意闭合曲线,我们都可以直观地认为,曲线把平面分割成了内、外两部分,其中“内”就是我们所谓的多边形区域。

基于这一认识,对于平面内任意一条直线,我们可以得出下面这些结论:

直线穿越多边形边界时,有且只有两种情况:进入多边形或穿出多边形。

在不考虑非欧空间的情况下,直线不可能从内部再次进入多边形,或从外部再次穿出多边形,即连续两次穿越边界的情况必然成对。

直线可以无限延伸,而闭合曲线包围的区域是有限的,因此最后一次穿越多边形边界,一定是穿出多边形,到达外部。

现在回到我们最初的题目。假如我们从一个给定的点做射线,还可以得出下面两条结论:

如果点在多边形内部,射线第一次穿越边界一定是穿出多边形。

如果点在多边形外部,射线第一次穿越边界一定是进入多边形。

把上面这些结论综合起来,我们可以归纳出:

当射线穿越多边形边界的次数为偶数时,所有第偶数次(包括最后一次)穿越都是穿出,因此所有第奇数次(包括第一次)穿越为穿入,由此可推断点在多边形外部。

当射线穿越多边形边界的次数为奇数时,所有第奇数次(包括第一次和最后一次)穿越都是穿出,由此可推断点在多边形内部。

到这里,我们已经了解了这个解法的思路,大家可以试着自己写一个实现出来。关于算法实现中某些具体问题和边界条件的处理,下次接着写,这次画图已经画够了……

后记:给出这个解法后,我简单搜了一下,原来这种算法就叫做射线法(ray casting)或者奇偶规则法(even odd rule),是一种早已被广泛应用的算法。后面还打算介绍另一种通过回转数(winding number,拓扑学的一个概念)解这个问题的思路。

二、射线法在实际应用中的一些问题和解决方案。

点在多边形的边上

前面我们讲到,射线法的主要思路就是计算射线穿越多边形边界的次数。那么对于点在多边形的边上这种特殊情况,射线出发的这一次,是否应该算作穿越呢?

看了上面的图就会发现,不管算不算穿越,都会陷入两难的境地——同样落在多边形边上的点,可能会得到相反的结果。这显然是不正确的,因此对这种特殊情况需要特殊处理。

点和多边形的顶点重合

这其实是第一种情况的一个特例。

射线经过多边形顶点

射线刚好经过多边形顶点的时候,应该算一次还是两次穿越?这种情况比前两种复杂,也是实现中的难点,后面会讲解它的解决方案。

射线刚好经过多边形的一条边

这是上一种情况的特例,也就是说,射线连续经过了多边形的两个相邻顶点。

解决方案:

判断点是否在线上的方法有很多,比较简单直接的就是计算点与两个多边形顶点的连线斜率是否相等,中学数学都学过。

点和多边形顶点重合的情况更简单,直接比较点的坐标就行了。

顶点穿越看似棘手,其实我们换一个角度,思路会大不相同。先来回答一个问题,射线穿越一条线段需要什么前提条件?没错,就是线段两个端点分别在射线两侧。只要想通这一点,顶点穿越就迎刃而解了。这样一来,我们只需要规定被射线穿越的点都算作其中一侧。

如上图,假如我们规定射线经过的点都属于射线以上的一侧,显然点D和发生顶点穿越的点C都位于射线Y的同一侧,所以射线Y其实并没有穿越CD这条边。而点C和点B则分别位于射线Y的两侧,所以射线Y和BC发生了穿越,由此我们可以断定点Y在多边形内。同理,射线X分别与AD和CD都发生了穿越,因此点X在多边形外,而射线Z没有和多边形发生穿越,点Z位于多边形外。

解决了第三点,这一点就毫无难度了。根据上面的假设,射线连续经过的两个顶点显然都位于射线以上的一侧,因此这种情况看作没有发生穿越就可以了。由于第三点的解决方案实际上已经覆盖到这种特例,因此不需要再做特别的处理

代码:

/*

射线法:判断一个点是在多边形内部,边上还是在外部,时间复杂度为O(n);

射线法可以正确用于凹多边形;

射线法是使用最广泛的算法,这是由于相比较其他算法而言,它不但可以正

确使用在凹多边形上,而且不需要考虑精度误差问题。该算法思想是从点出

发向右水平做一条射线,计算该射线与多边形的边的相交点个数,当点不在

多边形边上时,如果是奇数,那么点就一定在多边形内部,否则,在外部。

*/

#include

#include

#include

#include

using namespace std;

const int N = 2010;

const double eps = 1e-10;

const int INF = 0x3f3f3f3f;

//

struct point

{

double x, y;

point(double x=0, double y=0) : x(x), y(y){}

friend point operator - (const point& p1, const point& p2)

{

return point(p1.x-p2.x, p1.y-p2.y);

}

friend double operator ^ (const point& p1, const point& p2)

{

return p1.x*p2.y - p1.y*p2.x;

}

};

//

struct Segment

{

point s, e;

};

//

///判断一个double类型的数是  0  <0  >0;

int Sign(double x)

{

if( fabs(x) < eps )return 0;

if(x > 0)return 1;

return -1;

}

//

///判断o在ab的哪边;0:o在直线ab上; >0:在左边; <0:在右边;

double cross(point o, point a, point b)

{

return ((a-o)^(b-o));

}

//

///已知abc三点在一条直线上,判断点a是否在线段bc之间;<=0:在   >0:不在;

int Between(point a, point b, point c)

{

if(fabs(b.x-c.x) > fabs(b.y-c.y))

return Sign(min(b.x, c.x)-a.x)*Sign(max(b.x, c.x)-a.x);

else

return Sign(min(b.y, c.y)-a.y)*Sign(max(b.y, c.y)-a.y);

}

//

///判断点p0和线段S上,<=0:在,1:不在;

int PointOnSegment(point p0, Segment S)

{

if(Sign(cross(S.s, S.e, p0)) == 0)

return Between(p0, S.s, S.e);

return 1;

}

//

///求线段a和线段b的交点个数;

int SegmentCross(Segment a, Segment b)

{

double x1 = cross(a.s, a.e, b.s);

double x2 = cross(a.s, a.e, b.e);

double x3 = cross(b.s, b.e, a.s);

double x4 = cross(b.s, b.e, a.e);

if(Sign(x1*x2)<0 && Sign(x3*x4)<0) return 1;

if((Sign(x1)==0 && Between(b.s, a.s, a.e)<=0) ||

(Sign(x2)==0 && Between(b.e, a.s, a.e)<=0) ||

(Sign(x3)==0 && Between(a.s, b.s, b.e)<=0) ||

(Sign(x4)==0 && Between(a.e, b.s, b.e)<=0))

return 2;

return 0;

}

//

///判断点p0与含有n个节点的多边形的位置关系,p数组是顶点集合;

///返回0:边上或顶点上,    1:外面,   -1:里面;

int PointInPolygon(point p0, point p[], int n)

{

Segment L, S;

point temp;

L.s = p0, L.e = point(INF, p0.y);///以p0为起点的射线L;

int counts = 0;

p[n] = p[0];

for(int i=1; i<=n; i++)

{

S.s = p[i-1], S.e = p[i];

if(PointOnSegment(p0, S) <= 0) return 0;

if(S.s.y == S.e.y) continue;///和射线平行;

if(S.s.y > S.e.y) temp = S.s;

else temp = S.e;

if(PointOnSegment(temp, L) == -1)

counts ++;

else if(SegmentCross(L, S) == 1)

counts ++;

}

if(counts%2) return -1;

return 1;

}

//

int main()

{

point p[N];

int T, tCase = 1, n, q;

scanf("%d", &T);

while(T--)

{

scanf("%d", &n);

for(int i=0; i

scanf("%lf %lf", &p[i].x, &p[i].y);

scanf("%d", &q);

printf("Case %d:\n", tCase++);

for(int i=1; i<=q; i++)

{

int x, y;

scanf("%d %d", &x, &y);

int ans = PointInPolygon(point(x, y), p, n);

if(ans == 1) puts("No");

else puts("Yes");

}

}

return 0;

}

射线法 java_射线法(1190 - Sleepwalking )相关推荐

  1. 基于Matlab的跨孔层析成像的最短路径法弯曲射线追踪(二)

    基于Matlab的跨孔层析成像的最短路径法弯曲射线追踪(二) 在文(一)中展示了曲射线追踪结果和部分源代码,本文说明原理.[^1] 文章目录 基于Matlab的跨孔层析成像的最短路径法弯曲射线追踪(二 ...

  2. 基于Matlab的跨孔层析成像的最短路径法弯曲射线追踪(一)

    基于Matlab的跨孔层析成像的最短路径法弯曲射线追踪(一) CT技术是一种无损的工程物探检测技术,因其方法简单.分辨率高.理论上更可靠.结果更直观,被广泛的应用于各种工程.弯曲射线追踪是CT技术的一 ...

  3. 聊聊三维重建-条纹法之相位法(一)

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 本文由知乎作者Tengfei Jiang授权转载,不得擅自二次转载. 原文链接: https://zh ...

  4. 机器学习数据清洗之异常数据处理、标准差法、MAD法、箱图法、图像对比法、异常值处理准则

    机器学习数据清洗之异常数据处理.标准差法.MAD法.箱图法.图像对比法.异常值处理准则 目录

  5. 多目标决策问题1.1.1:线性加权法——熵权法确定权重

    多目标决策问题是目前数学建模中比较普遍的一类问题, 此类问题要求我们满足多个目标函数最优与决策变量的线性约束条件或非线性约束条件下进行求解, 多目标决策问题主要有主要目标法.线性加权法.分层序列法.步 ...

  6. 【运筹学】运输规划、表上作业法总结 ( 运输规划模型 | 运输规划变量个数 | 表上作业法 | 最小元素法 | 差额 Vogel 法 ★ | 闭回路法 ) ★★★

    文章目录 一.运输规划模型 1.产销平衡模型 2.产销不平衡模型 二.运输规划数学模型变量个数 三.表上作业法 四.表上作业法 : 求初始基可行解 1.最小元素法 2.差额法 ( Vogel ) 推荐 ...

  7. 【DBMS 数据库管理系统】数据仓库中 数据追加 ( 时标方法 | DELTA 文件法 | 前后映像文件法 | 日志文件法 )

    文章目录 一.数据追加 概念 和 方法 二.时标方法 三.DELTA 文件法 四.前后映像文件法 五.日志文件法 一.数据追加 概念 和 方法 数据仓库 数据追加 : "数据追加" ...

  8. TF之CNN:CNN实现mnist数据集预测 96%采用placeholder用法+2层C及其max_pool法+隐藏层dropout法+输出层softmax法+目标函数cross_entropy法+

    TF:TF下CNN实现mnist数据集预测 96%采用placeholder用法+2层C及其max_pool法+隐藏层dropout法+输出层softmax法+目标函数cross_entropy法+A ...

  9. C语言十个字母用冒泡法排序,冒泡排序法(C语言) - osc_wq8j2a9a的个人空间 - OSCHINA - 中文开源技术交流社区...

    常用的排序方法有冒泡排序法,选择排序法,插入排序法以及希尔排序法等.本文着重讲解如何利用C代码,实现冒泡排序. 首先,要了解什么是冒泡排序.冒泡排序是常用的一种排序方法,其基本方法就是逐次比较.即一次 ...

最新文章

  1. php读取文件和读取redis,PHP结合redis实现大文件去重
  2. Linux上在文件夹上层新建一个同名目录
  3. js获取一周从开始到结束日期范围
  4. python中isalpha的用法_python函数--isalpha()方法
  5. http:(2):http请求方法
  6. B. Code For 1 一个类似于线段树的东西
  7. Linux Shell快速入门
  8. UI标签库专题二:JEECG智能开发平台Column(列) 子标签
  9. 星巴克“猫爪杯”遭国人疯抢 而 “大白兔冰淇淋”在美国大火
  10. python的ogr模块_Python与开源GIS:使用OGR模块打开矢量数据
  11. pdf论文中visio画的图出现Times New Roman 字体未嵌入
  12. NETTY keeplive 参数,心跳检测
  13. Android NDK开发1——开发流程+依赖外部so+生成自实现so+静态注册JNI+动态注册JNI
  14. python从excel读取数据用matplotlib画平面折线图
  15. Linux vim 编辑文件底部显示[converted]解决办法
  16. ios 开发 flurry 资料
  17. 12款常用的Web服务器软件整理(windows+Linux)
  18. 以太坊大厦将倾?老白:EOS不会取代以太坊,各有应用场景
  19. 2016年华为优招面试经验
  20. 数据库批量插入和存在的问题

热门文章

  1. linux 编译openmp,Linux 系统中OpenMP
  2. 天润融通java面试_【天润融通面试|面试题】-看准网
  3. 怎么让图片一直转圈_鼠标为什么一直在转圈?如何强制关闭程序?
  4. 随机森林和决策树区别_第六讲 决策树与随机森林
  5. python gui漂亮_python 漂亮的gui
  6. 试题以文件的形式存在C语言,2016年计算机二级C语言上机题库
  7. 计算机电缆和控制电缆区别,动力电缆和控制电缆有啥区别?
  8. java里冒泡排序编程案例_java编程题:用Java实现一个冒泡排序算法
  9. xheditor 内容保存时 不转义html特殊字符,xheditor编辑器上传图片(示例代码)
  10. php的filter input,记一个php://filter和php://input的CTF题