CodeVS1302 小矮人 解题报告【计算几何】【凸包】【凸包与直线判交】

本文介绍了一种算法,用于判断给定点集形成的凸包与直线的位置关系,以解决矮人村落避免高速公路穿越的问题。通过构造凸包并利用向量叉乘判断直线是否穿过凸包。

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

题目描述 Description
矮人们平时有走亲访友的习惯。一天,矮人国要修一条高速公路,矮人们希望他们走亲访友的时候,能够不必穿越高速公路,这样会更安全一些。现在有M个高速公路的修建方案,请你判断这M条高速功能是否能满足矮人们的期望。也就是说给出平面上的N个点(矮人们的住所位置),对于M条直线(高速公路),依次判断这N个点是否在每条直线的同一侧。是输出GOOD,不是输出BAD。
N,M≤100000
输入描述 Input Description
第一行一个整数N,表示矮人的住所数。
接下来N行每行一个坐标代表矮人的住所坐标。
接下来的若干行(到文件结尾)每行4个整数,代表高速公路上的2个点。
所有坐标均在-109到109之间
输出描述 Output Description
对合法的方案输出GOOD,否则输出BAD。
样例输入 Sample Input
4
0.0 0
6.00 -0.001
3.125 4.747
4.747 0.47
5 3 7 0
4 -4.7 7 4.7
4 47 4 94
样例输出 Sample Output
GOOD
BAD
BAD
解题报告
这道题的题意是给我们n个点,依次给m每个边,问这n个点是否在这条边的同一侧。
我们很容易想到,要把这n个点看做一个整体,最好的办法莫过于搞一个凸包把这n个囊括其中。然后判断这条直线是否穿过这个凸包。
怎样判定直线是否穿过凸包呢?如果一条直线穿过这个凸包,这条直线一定在凸包两个最远点的中间(向量叉乘小于零)。怎样找最远点呢?
二分查找第一个大于直线斜率的边,它的起点一定是一个最远点,因为他后面的边斜率比它大,也就相当于走的离凸包中心越来越近。然后把斜率取相反数,再找第一个斜率大于它的边,起点是另一个最远点。
代码如下:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define NAME ""
#define Point Vector
using namespace std;
const double eps=1e-8;
const double PI=acos(double(-1));
const int N=100000;
struct Vector
{
    double x,y;
    double len(){return sqrt(x*x+y*y);}
    double ang(){double ret=atan2(y,x);return ret<-PI/2?ret+2*PI:ret;}
    Vector(int a=.0,int b=.0):x(a),y(b){}
    Vector operator+(const Vector &s)const{return Vector(x+s.x,y+s.y);}
    Vector operator-(const Vector &s)const{return Vector(x-s.x,y-s.y);}
    Vector operator*(int s)const{return Vector(x*s,y*s);}
    Vector operator/(int s)const{return Vector(x/s,y/s);}
    bool operator<(const Point &s)const
    {
        if(s.x==x)return y<s.y;
        else return x<s.x;
    }
}p[N+5],ch[N+5];
int n;
double ang[N+5];
double cross(Vector a,Vector b){return a.x*b.y-a.y*b.x;}//向量叉积 
int sign(double a){return a>eps?1:(a<-eps?-1:0);}
bool cmp(double a,double b){return sign(b-a)==1;}
bool onleft(Point a,Point b,Point p)
{
    return sign(cross(b-a,p-a))>0;
}
int ComvexHull()
{
    sort(p+1,p+n+1);
    int m=0;
    for(int i=1;i<=n;++i)
    {
        while(m>1&&cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-1;i>=1;--i)
    {
        while(m>k&&cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    if(n>1) m--;
    return m;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        double x,y;
        scanf("%lf%lf",&x,&y);
        p[i]=Point(x,y);
    }
    int m=ComvexHull();
    for(int i=0;i<m;i++)ang[i]=(ch[i+1]-ch[i]).ang();
    Point A,B;
    while(scanf("%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y)==4)
    {
        if(n<=1)printf("GOOD\n");
        else
        {
            Point u=ch[upper_bound(ang,ang+m,(B-A).ang(),cmp)-ang];
            Point v=ch[upper_bound(ang,ang+m,(A-B).ang(),cmp)-ang];
            if(sign(cross(B-A,u-A)*cross(B-A,v-A))<eps)printf("BAD\n");
            else printf("GOOD\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值