POJ1912_A highway and the seven dwarfs_判断凸包与直线是否相交

该博客讨论了POJ1912问题,即给定n个点和多条直线,判断每条直线是否与点集的凸包相交。当n<=1时直接判断所有点在同一侧,否则利用凸包极角单调递增的性质,通过二分查找找到距离直线最远的两点,并判断它们是否在直线同侧来确定直线是否穿过凸包。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给出 n 个点和若干条直线,每一条直线用两个点的形式给出。对每一条直线,问所有点是不是在这条直线的同一侧。题目保证直线不会经过n个点。

思路

首先就有一个特殊情况。因为 n >= 0。当 n <= 1 的时候,可以直接判定为对每一条直线,n个点都在它的同一侧。
对于n > 1的情况,显然是求凸包与直线是否相交的问题。这里利用了凸包的一个性质。
对于凸包上相邻的两个点构成的向量的极角,取 [-pi/2, 3/2pi]时,是单调递增的。
求出凸包上边的极角后,
用二分法,找出极角分别大于给出直线的两个方向向量的第一个向量, 这两个向量的起点,就是在垂直于这条直线的方向上距离最远的两个点。
只需要判断这两个点是否在直线的同一侧,就能判断这条直线是不是穿过凸包。

可以画画图理解一下。

链接

http://poj.org/problem?id=1912

代码

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>

using namespace std;

const int maxn = 1e5 + 10;
const double pi = acos(-1.0);

struct P{
    double x, y;

    P(){}

    P(double x, double y): x(x), y(y){}

    bool operator < (const P &a) const{
        if(x != a.x) return x < a.x;
        return y < a.y;
    }

    P operator - (const P &a) const{
        return P(x - a.x, y - a.y);
    }

    double det(const P &a) const{
        return x * a.y - y * a.x;
    }

    double angle(){
        double angle = atan2(y, x);
        if(angle < -pi / 2) angle += 2 * pi;
        return angle;
    }
};

int n;
P ps[maxn], qs[maxn];
double angle[maxn];
P p1, p2;
int tb;

int convex_hull(){
    sort(ps, ps + n);
    int k = 0;

    for(int i = 0; i < n; ++i){
        while(k > 1 && (qs[k-1] - qs[k-2]).det(ps[i] - qs[k-1]) <= 0) --k;
        qs[k++] = ps[i];
    }

    for(int i = n - 2, t = k; i >= 0; --i){
        while(k > t && (qs[k-1]- qs[k-2]).det(ps[i] - qs[k-1]) <= 0) --k;
        qs[k++] = ps[i];
    }

    return k-1;
}

inline int getp(double a){
    return lower_bound(angle, angle + tb, a) - angle;
}

bool inter(){
    if(n <= 1) return false;

    int a = getp((p1 - p2).angle());
    int b = getp((p2 - p1).angle());
    if((p1 - p2).det(qs[a] - p2) * (p1 - p2).det(qs[b] - p2) <= 0) return true;
    return false;
}

int main(){
//  freopen("in.txt", "r", stdin);

    scanf("%d", &n);
    for(int i = 0; i < n; ++i){
        scanf("%lf %lf", &ps[i].x, &ps[i].y);
    }

    tb = convex_hull();

    for(int i = 0; i < tb; ++i){
        angle[i] = (qs[i+1] - qs[i]).angle();
    }

    while(scanf("%lf %lf %lf %lf", &p1.x, &p1.y, &p2.x, &p2.y) == 4){
        if(inter()) puts("BAD");
        else puts("GOOD");
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值