现在的位置: 首页 > 综合 > 正文

FZU1120 A Pilot in Danger! 判断点是否在多边形中

2013年04月14日 ⁄ 综合 ⁄ 共 3293字 ⁄ 字号 评论关闭

判断点是否在多边形中

判断点P是否在多边形中是计算几何中一个非常基本但是十分重要的算法。以点P为端点,向左方作射线L,由于多边形是有界的,所以射线L的左端一定在多边形外,考虑沿着L从无穷远处开始自左向右移动,遇到和多边形的第一个交点的时候,进入到了多边形的内部,遇到第二个交点的时候,离开了多边形,……所以很容易看出当L和多边形的交点数目C是奇数的时候,P在多边形内,是偶数的话P在多边形外。

但是有些特殊情况要加以考虑。如图下图(a)(b)(c)(d)所示。在图(a)中,L和多边形的顶点相交,这时候交点只能计算一个;在图(b)中,L和多边形顶点的交点不应被计算;在图(c)和(d) 中,L和多边形的一条边重合,这条边应该被忽略不计。如果L和多边形的一条边重合,这条边应该被忽略不计。

为了统一起见,我们在计算射线L和多边形的交点的时候,1。对于多边形的水平边不作考虑;2。对于多边形的顶点和L相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略;3。对于P在多边形边上的情形,直接可判断P属于多边行。由此得出算法的伪代码如下:
    count ← 0;
    以P为端点,作从右向左的射线L; 
    for 多边形的每条边s
     do if P在边s上 
          then return true;
        if s不是水平的
          then if s的一个端点在L上
                 if 该端点是s两端点中纵坐标较大的端点
                   then count ← count+1
               else if s和L相交
                 then count ← count+1;
    if count mod 2 = 1 
      then return true;
    else return false;
其中做射线L的方法是:设P'的纵坐标和P相同,横坐标为正无穷大(很大的一个正数),则P和P'就确定了射线L。

判断点是否在多边形中的这个算法的时间复杂度为O(n)。

另外还有一种算法是用带符号的三角形面积之和与多边形面积进行比较,这种算法由于使用浮点数运算所以会带来一定误差,不推荐大家使用。

http://acm.fzu.edu.cn/problem.php?pid=1120

代码

#include <stdio.h>
#include
<stdlib.h>
#include
<math.h>
#define MaxNode 50
#define INF 999999999
int flag[50050];

typedef
struct TPoint {
double x;
double y;
} TPoiont;

typedef
struct TSegment {
TPoint p1;
TPoint p2;
} TSegment;

typedef
struct TPolygon {
TPoint point[MaxNode];
int n;
} TPolygon;

double multi(TPoint p1, TPoint p2, TPoint p0) {
//求矢量[p0, p1], [p0, p2]的叉积
//p0是顶点
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
//若结果等于0,则这三点共线
//若结果大于0,则p0p2在p0p1的逆时针方向
//若结果小于0,则p0p2在p0p1的顺时针方向
}

double max(double x, double y) {
//比较两个数的大小,返回大的数
if (x > y) return x;
else return y;
}

double min(double x, double y) {
//比较两个数的大小,返回小的数
if (x < y) return x;
else return y;
}

bool Intersect(TSegment L1, TSegment L2) {
//线段l1与l2相交而且不在端点上时,返回true
//判断线段是否相交
//1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
TPoint s1 = L1.p1;
TPoint e1
= L1.p2;
TPoint s2
= L2.p1;
TPoint e2
= L2.p2;
//2.跨立试验
if ((max(s1.x, e1.x) > min(s2.x, e2.x)) &&
(max(s2.x, e2.x)
> min(s1.x, e1.x)) &&
(max(s1.y, e1.y)
> min(s2.y, e2.y)) &&
(max(s2.y, e2.y)
> min(s1.y, e1.y)) &&
(multi(s2, e1, s1)
* multi(e1, e2, s1) > 0) &&
(multi(s1, e2, s2)
* multi(e2, e1, s2) > 0)
)
return true;
return false;
}

bool Online(TSegment L, TPoint p) {//p在L上(不在端点)时返回true
//1.在L所在的直线上 2.在L为对角线的矩形中
double dx, dy, dx1, dy1;
dx
= L.p2.x - L.p1.x;
dy
= L.p2.y - L.p1.y;
dx1
= p.x - L.p1.x;
dy1
= p.y - L.p1.y;
if (dx * dy1 - dy * dx1 != 0) return false; //叉积
if (dx1 * (dx1 - dx) < 0 || dy1 * (dy1 - dy) < 0) return true;
return false;
}

bool same1(TSegment L, TPoint p1, TPoint p2) { //判断p1, p2是否在L的同侧
if (multi(p1, L.p2, L.p1) * multi(L.p2, p2, L.p1) < 0) return true;
return false;
}

bool Inside(TPoint q, TPolygon polygon) {
int c, i;
TSegment L1, L2;
c
= 0;
L1.p1
= q;
L1.p2
= q;
L1.p2.x
= INF;
/*(1)相交1.p[i]和p[i+1]在L的两侧2.p[i]和p[i+2]在L的同侧
* 3.p[u]和p[i+3]在L的同侧或异侧
*/
for (i = 1; i <= polygon.n; i++) {
L2.p1
= polygon.point[i];
L2.p2
= polygon.point[i + 1];
if (Intersect(L1, L2)) {
c
++;
continue;
}
if (!Online(L1, polygon.point[i + 1])) continue;
if (!Online(L1, polygon.point[i + 2]) &&
!same1(L1, polygon.point[i], polygon.point[i + 2])) {
c
++;
continue;
}
if (Online(L1, polygon.point[i + 2]) &&
!same1(L1, polygon.point[i], polygon.point[i + 3]))
c
++;
}
if (c % 2 == 0) return false;
else return true;
}

int main() {
int i, test, k;
int primp, primq;
TPoint p;
p.x
= 0;
p.y
= 0;
test
= 1;
TPolygon polygon;
while (scanf("%d", &polygon.n) != EOF && polygon.n) {
printf(
"Pilot %d\n", test++);
for (i = 1; i <= polygon.n; i++) {
scanf(
"%lf%lf", &polygon.point[i].x, &polygon.point[i].y);
}
scanf(
"%d%d", &primp, &primq);
if (Inside(p, polygon)) {
printf(
"The pilot is in danger!\n");
k
= (primp - 1) * (primq - 1) / 2;
printf(
"The secret number is %d.\n", k);
}
else printf("The pilot is safe.\n");
printf(
"\n");
}
return 0;
}

 

【上篇】
【下篇】

抱歉!评论已关闭.