HRBUST1429—凸多边形(点是否在多边形之内)

HRBUST1429—凸多边形

Description

已知一个凸多边形A(包含n个点,点按照顺时针给出),和一个点集B(包含m个点),请判断这m个点是否都严格在凸多边形A内部。

Input
输入包含多组测试数据。
对于每组测试数据:
第1行,包含一个整数n (3 ≤ n ≤ 105)代表着凸多边形A的点的数量。
接下来n行每行包含一个坐标(x, y) (-109 ≤ x, y ≤ 109) 表示这个凸多边形,点按照顺时针给出。
第n + 2行,包含一个整数m (3 ≤ m ≤ 105)代表着点集B的点的数量。
接下来m行每行包含一个坐标(x, y) (-109 ≤ x, y ≤ 109) 表示这个点集B。
处理到文件结束

Output
对于每组测试数据:
第1行,如果点集B都严格在凸多边形A内,输出YES,否则输出NO。

Sample Input
4
-10 -10
-10 10
10 10
10 -10
3
0 0
1 1
2 2
4
-10 -10
-10 10
10 10
10 -10
3
100 100
1 1
2 2

Sample Output
YES

NO

【思路分析】(参考http://blog.csdn.net/lttree/article/details/24291719

该题是判断一个点集是否全部严格在凸多边形之内,这里先介绍一个“二分法”:

其步骤为:

1、选取多边形其中一点作为原点,与其他各点相连并作射线,会得到很多三角形区域

 2、判断点集中的一点是否在以多边形原点为端点的最两侧的射线之外,若是,则该点在多边形之外;

3、若该点不满足2中的条件,则继续判断该点是在哪两个相邻的射线区域之内,这里便用到了二分法:

首先定义三个变量,low = 2,high = n - 1,mid = (low + high) / 2。先判断该点是在多边形最中间的射线(p0pmid)的左边还是右边,若在左,令high = mid;

若在右,令low = mid + 1。直到low > high 为止。

4、最后判断该点是否在三角形区域之内,即判断该点与三角形的第三条边和该点的位置关系。设点为a,三角形第三条边两端点分别为pm、p(m + 1),则等价于判断点a在向量pmp(m + 1)的顺时针还是逆时针,若是顺时针,则该点在多边形之内。


这里再自我补充一下判断点和直线的位置关系的方法:

设多边形上两相邻的点分别为p0 、p1,需要判断的点为p2,定义一个叉积函数cross

double cross(Point p0,Point p1,Point p2)

{

   return (p1.x - p0.x)*(p2.y - p0.y) -(p1.y - p0.y)*(p2.x - p0.x);

}

若cross(p0,p1,p2)为正,表示点p2在向量p0p1的逆时针方向,若cross(p0,p1,p2)为负,表示点p2在向量p0p1的顺时针方向,若cross(p0,p1,p2)为零,表示p2在向量p0p1之上。


代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 100005;
struct Point
{
    double x;
    double y;
}pointOfPolygon[maxn],pointOfPoint[maxn];

double cross(Point p0,Point p1,Point p2)
{
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}

int main()
{
    int n,m;
    while(scanf("%d",&n) != EOF)
    {
        for(int i = 0;i < n;i++)
        {
            scanf("%lf %lf",&pointOfPolygon[i].x,&pointOfPolygon[i].y);
        }
        scanf("%d",&m);
        for(int i = 0;i < m;i++)
        {
            scanf("%lf %lf",&pointOfPoint[i].x,&pointOfPoint[i].y);
        }
        int flag = 0;
        for(int i = 0;i < m;i++)
        {
            if(cross(pointOfPolygon[0],pointOfPolygon[1],pointOfPoint[i]) >= 0 ||
               cross(pointOfPolygon[0],pointOfPolygon[n - 1],pointOfPoint[i]) <= 0 )
            {//判断该点是否在多边形两最外侧边上及侧边外
                flag = 1;
                break;
            }
            int low = 2;
            int high = n - 1;
            int mid;
            while(low < high)//二分确定区域
            {
                mid = (low + high) >> 1;
                if(cross(pointOfPolygon[0],pointOfPolygon[mid],pointOfPoint[i]) > 0)
                {
                    high = mid;
                }
                else
                {
                    low = mid + 1;
                }

            }
            if(cross(pointOfPolygon[low],pointOfPolygon[low - 1],pointOfPoint[i]) <= 0)
            //判断点和三角形第三条边的位置关系
            {
                flag = 1;
                break;
            }

        }
        if(flag == 1)
            printf("No\n");
        else
            printf("Yes\n");
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值