Hust oj 1429 凸多边形(叉乘+二分)

凸多边形
Time Limit: 2000 MSMemory Limit: 65536 K
Total Submit: 276(61 users)Total Accepted: 101(52 users)Rating: Special Judge: No
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


正常暴力用叉乘跑的话肯定会超时,这里学习到了一种新姿势解决这类问题

计算几何之判断点是否在多边形内,

判断点是否在多边形内有多种方法:射线法,角度和判断法,改进弧长法还有这次用到的二分法。

前三者的时间复杂度均为O(n),此方法复杂度仅为O(logn)。

而且对于判断很多点是否在多边形内,就可以用这种方法了,耗时少。

原理:

将一个多边形,以其中一个点为原点,开始与其他各点相连并延长做射线,则会形成许多个三角形区域。(如左图)

 

这样我们可以先判断点在哪两条向量之间。用二分查找,可以很快搜索到。

当然,首先要判断点是否在最左边向量左侧或者最右边向量右侧,如是,则点不在多边形内。

以右图为例,我们找到紫色点在左数第一个三角形区域内,绿色点在左数第二个三角形区域内。

然后,再判断下图所示线段与 所判断点的位置关系。

 

绿色的线段可以判断绿色的点,左边紫色的点也由相应的线段来判断位置关系。

这样可以判断点是否在多边形内啦。

总结一下:

①建立一个个三角形区域,用其中两条边判断点所在大体区域。

②用第三条边来判断点是否在多边形内。

二分查找就是用在了第一条的地方,用来查找大体区域位置。

明白了这个,就可以做相应的题目来练习一下了!

就是这道题~。~

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;

typedef long long LL;
const int Maxn = 100005;
struct Point
{
    LL x;
    LL y;
}a[Maxn],b[Maxn];
LL n,m;

LL 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()
{
    while(~scanf("%lld",&n))
    {
        for(int i=0;i<n;i++)
        {
            scanf("%lld%lld",&a[i].x,&a[i].y);
        }
        scanf("%lld",&m);
        for(int i=0;i<m;i++)
        {
            scanf("%lld%lld",&b[i].x,&b[i].y);
        }
        int flag = 0;
        for(int i=0;i<m;i++)
        {
            if(cross(a[0],a[1],b[i]) >= 0 || cross(a[0],a[n-1],b[i]) <= 0)
            {
                flag = 1;
                break;
            }
            int left = 1;
            int right = n - 1;
            while(left < right)
            {
                int mid = (right + left) >> 1;
                if(cross(a[0],a[mid],b[i]) > 0)
                    right = mid;
                else
                    left = mid + 1;
            }
            if(cross(a[left],a[left-1],b[i]) <= 0)
            {
                flag = 1;
                break;
            }
        }
        if(flag)
            printf("NO\n");
        else
            printf("YES\n");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值