判断点是否在凸包内

周测5
描述
二维平面上,给定n个点{ai}和m个点{bi},且保证这n+m个点中,任意两个点的x坐标或y坐标均不相同。
对于每个bi,判断是否存在由3个ai,aj,ak(1≤i,j,k≤n,i≠j≠k)点组成的三角形包含bi(在三角形边上也算包含;允许三点共线的三角形,此时只有bi在三点中任意两点的线段上才算包含)。
输入
第一行为一个整数n。接下来n行,其中第i行有两个整数,表示ai的横纵坐标。
第一行为一个整数m。接下来m行,其中第i行有两个整数,表示bi的横纵坐标。
输出
输出m行,第i行为一个整数0或1,分别表示是否存在一个三角形包含该bi。
样例1输入

3
1 -6
-10 -1
0 6
3
-2 7
-4 -2
-3 1

样例1输出

0
1
1

样例1解释
如图,绿点为A,红点为B。2号、3号红点均包含于3个绿点里。

判断点是否在凸包内_第1张图片
样例2
请查看下发文件内的sample2_input.txt和sample2_output.txt以及draw.py。
为了大家调试方便,我特意提供了一个draw.py的文件来绘制点集。大家需要安装python 3,然后
python3 -m pip install matplotlib
最后用
python3 draw.py
运行,将题中数据复制进去即可得到图片。(当然你也可以重定向数据)
限制
n,m≥3,坐标绝对值不超过 109
其中30%的数据,n,m≤200;
另外30%的数据,n,m≤2000;
剩下40%的数据,n,m≤300000。
时间:3 sec
空间:256 MB
注意,使用python的同学,OJ给你们提供了pypy来提速,源代码根本不用变,只需在第一行修改一下即可享受高速python。
使用pypy必须得在第一行加上(或者直接使用我给你们的IO模板)
python 2:
#!/usr/bin/env pypy
python 3:
#!/usr/bin/env pypy3
提示
[ 凸包,可以参考 选做题的《凸包》那题代码 ]
为了帮助大家完成题目,我们提供了只包含了输入输出功能的程序模板。
你可以根据自己的实际情况,在这些程序的基础上进行作答,或不参考这些程序,这将与你的得分无关。
这些程序可以从【这里】下载。

法一助教方法:

1.先求出上下凸包,然后二分求出上、下凸壳与x=x0相交的两条逆时针有向边e1、e2(若不相交,则红点不在凸包内)
2.然后判断红点(x0,y0)是否同时在e1、e2的左边,若是则在凸包内,否则不在。
时间复杂度为O((n+m)logn)

我的方法以及代码如下:
https://blog.csdn.net/qq_36782366/article/details/78161807

这里解释下判断点是否在凸包内的模板: 一个点的复杂度为O(logn)复杂度
就是取其中的一个点,他和其他点可以组成n-2个三角形,利用二分判断差积,判断他是在当前三角形内,还是在三角形左边,还是在三角形右边,或者是三角形外。

#include 
#include 
#include 
#include 
#include 
using namespace std;


// ================= 代码实现开始 =================

struct ip {
    int x, y, i;
    ip(int x = 0, int y = 0) : x(x), y(y), i(0) { }
    //上面等式为类的初始化列表
    void ri(int _i) {
        scanf("%d%d", &x, &y);
        i = _i;
    }
};

typedef ip iv;

const int N = 300005;
ip a[N], b[N], ch[N];
//a[]存储需要构成凸包的n个点对,b[]存储需要进行判断内外的m个点对 
//convexhull,ch[]存储实际的凸包边界点

// 先比较x轴再比较y轴
bool operator < (const ip &a, const ip &b) {
    return a.x == b.x ? a.y < b.y : a.x < b.x;
}

// 重载减号,用a - b来计算两点相减得到的向量
iv operator - (const ip &a, const ip &b) {
    return ip(a.x - b.x, a.y - b.y);
}

// 重载乘号,用a * b来计算a和b的叉积(外积)
long long operator * (const iv &a, const iv &b) {
    return (long long)a.x * b.y - (long long)a.y * b.x;
}

int convex(ip *a, int n) {  
    //升序排列
    sort(a,a+n);
    // n = unique(a,a+n)-a; //若题目中有重复点,必须去重
    
    int m=0;
    //求下凸壳
    for(int i=0; i1 && ((ch[m-1]-ch[m-2])*(a[i]-ch[m-2])) < 0; --m);
        ch[m++] = a[i];
    }
    
    //求上凸壳
    for(int i=n-2, t=m; i>=0; --i){
        for(; m>t && ((ch[m-1]-ch[m-2])*(a[i]-ch[m-2])) < 0; --m);
        ch[m++] = a[i];
    }
    
    return m-1 ; 
    //返回值为凸包内点的数目
}

//这里解释下判断点是否在凸包内的模板:
//就是取其中的一个点,他和其他点可以组成n-2个三角形,利用二分判断差积,
//判断他是在当前三角形内,还是在三角形左边,还是在三角形右边,或者是三角形外。
int check(ip b,int n){
    int l=1,r=n-2,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        long long a1=(ch[mid]-ch[0])*(b-ch[0]);  //ch0->chmid 与 ch0->b 的叉积
        long long a2=(ch[mid+1]-ch[0])*(b-ch[0]);//ch0->chmid+1 与 ch0->b 的叉积
        if(a1>=0&&a2<=0)    //也就是说此时该点b在chmid上方或其线上,又在chmid+1下方或其线上
        {
            if((ch[mid+1]-ch[mid])*(b-ch[mid])>=0) //chmid->chmid+1 与 chmid->b 的叉积
                return true;        //如果此时叉积大于等于0,说明该点b在该三角形内部或者边界上
            return false;           //反之该点在三角形外部
        }
        else if(a1<0)  //如果该点在chmid下方,那么把上边界缩小到mid-1
        {
            r=mid-1; //二分的端点问题需要重视!
        }
        else //如果该点在chmid+1上方,那么把下边界拉至mid+1
        {
            l=mid+1;
        }
    }
    return false;
}

// 返回一个数组,依次表示答案
vector getAnswer(int n, ip *a, int m, ip *b) {
    vector ans;
    int mm = convex(a, n); //m为ch[]数组的长度
    for(int i=0; i ans = getAnswer(n, a, m, b);
    for (int i = 0; i < m; ++i)
        printf("%d\n", ans[i]);
    return 0;
}

你可能感兴趣的:(CS精英挑战营,算法,计算几何)