codevs 1302 小矮人 凸包 解题报告

题目描述 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

思路

首先可以想出一个凸包模型来,因为求出所有点的凸包后可以判断直线如果穿过凸包,就一定不满足题意。
问题是怎么判断直线是否穿过凸包呢?
如果直线穿过凸包,就一定在凸包两个最远点的中间,最远点可以通过下面的方法找:首先预处理记录下凸包每个边的斜率,再算出直线的斜率,将斜率从小到大排列,假设直线斜率是正的,二分查找第一个大于直线斜率的边,它的起点一定是一个最远点,可以画图验证一下,因为他后面的边斜率比它大,也就相当于走的离凸包中心越来越近。然后把斜率取相反数,再找第一个斜率大于它的边,起点是另一个最远点。

在实现时直接用atan2()函数计算角度不用算斜率。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;
const int N=100000+10;
const double PI=acos(double(-1));
const double eps=1e-8;
int n;
double ang[N];
struct Point 
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y) {}
};
Point p[N],ch[N];
Point operator +(Point A,Point B) {return Point(A.x+B.x,A.y+B.y);}
Point operator -(Point A,Point B) {return Point(A.x-B.x,A.y-B.y);}
Point operator *(Point A,double p) {return Point(A.x*p,A.y*p);}
Point operator /(Point A,double p) {return Point(A.x/p,A.y/p);}
bool operator <(const Point& a,const Point& b) 
{return a.x<b.x||(a.x==b.x&&a.y<b.y);}
int sign(double x) 
{
    if (fabs(x)<eps) return 0;
    else if (x<0) return -1;
    else return 1;
}
bool sign2(const double& a,const double& b) 
{
    if (sign(b-a)==1) return 1;
    return 0;
}
bool operator ==(const Point& a,const Point& b) 
{return sign(a.x-b.x)==0&&sign(a.y-b.y)==0;}
double angle(Point v) 
{ 
    double ret=atan2(v.y,v.x); 
    return ret<-PI/2?ret+2*PI:ret; 
}
double Cross(Point A,Point B) 
{return A.x*B.y-A.y*B.x;}
int ConvexHull() 
{
    sort(p,p+n);
    int m=0;
    for (int i=0;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-2;i>=0;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=0;i<n;i++) 
    scanf("%lf%lf",&p[i].x,&p[i].y);
    int c=ConvexHull();
    for (int i=0;i<c;i++) 
    ang[i]=angle(ch[i+1]-ch[i]);
    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+c,angle(b-a),sign2)-ang];
            Point v=ch[upper_bound(ang,ang+c,angle(a-b),sign2)-ang];
            if (sign(Cross(b-a,u-a)*Cross(b-a,v-a))<eps) 
            printf("BAD\n");
            else printf("GOOD\n");
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值