A highway and the seven dwarfs POJ - 1912凸包+旋转卡壳+极角排序+离线(二分凸包线段的角度)

是真的激动,这题写了整整两天,当我看到ac的时候是真的开心,可能这就是acm的魅力把,真的好开心啊

其中无数次我想要放弃,想着算了还是看看题解那种二分法把,但是最后还是坚持下来了,拿到了数据对着数据debug了一下

错在了一个很小的细节。

这题解法:凸包,选择卡壳,离线

首先呢,思路:判断一堆的点是不是在一条直线的一边

我的过程:首先拿到手,二话不说暴力起来,好啦tle一发然后想了一下构建凸包这下应该ac了把,然后又tle了一发,开始思考人生,然后看了一下题解,那个二分我没看太懂,但是我感觉可以用选择卡壳找出那一条线段,因为在图上画一下可以得出其实我们首先把所有的线段按atan2(在第三和第四象限的角转化为第一和第二象限)来排序之后,会发现从第一条线段到最后一条线段的过程不就是线段绕着凸包旋转的过程嘛!!!于是果断用类似于选择卡壳的方法进行旋转这样就可以将原来(n2)的复杂度降低为线性的复杂度,然后因为我们排序了线段所以要将线段离线处理输出结果!!!说实话我的这个方法真的不是很好,但是在这个过程中加深了对选择卡壳的理解,然后其实这个方法跟二分是差不多的,我们其实可以将凸包的边按atan排序然后会发现线段刚好夹在了第一象限第一个角度比他大的边和第四象限第一个角度比他大的边中间,这两条线段可以用二分搜出!!!

贴代码!!!

///更新

之前的想法现在看来蠢得一批

直接二分角度就可以

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>

using namespace std;

const int maxn=111111l;
const double eps=1e-8;

int sgn(double x)
{
    if(fabs(x)<eps)
        return 0;
    if(x<0)
        return -1;
    else
        return 1;
}

struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y)
    {
        x=_x;
        y=_y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    double operator ^(const Point &b)const
    {
        return x*b.y-y*b.x;
    }
    double operator *(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
};

struct Line
{
    Point s,e;
    int index;
    char str[10];
    Line(){}
    Line(Point _s,Point _e)
    {
        s=_s;
        e=_e;
    }
};
Point p[maxn];
Point p0[maxn];
int n;

double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}

bool cmp(Point a,Point b)
{
    int ans=sgn((a-p[0])^(b-p[0]));
    if(ans==1)
        return  true;
    else if(ans==0)
        return dist(a,p[0])<dist(b,p[0]);
    else
        return false;
}

int Stack[maxn];
int top;
void graham()
{
    for(int i=0;i<n;i++)
    {
        if(p[i].x<p[0].x||(p[i].x==p[0].x&&p[i].y<p[0].y))
            swap(p[i],p[0]);
    }
    sort(p+1,p+n,cmp);
    if(n==1)
    {
        Stack[0]=0;
        top=1;
    }
    else if(n==2)
    {
        Stack[0]=0;
        Stack[1]=1;
        top=2;
    }
    else
    {
        Stack[0]=0;
        Stack[1]=1;
        top=2;
        for(int i=2;i<n;i++)
        {
            while(top>1&&sgn((p[Stack[top-1]]-p[Stack[top-2]])^(p[i]-p[Stack[top-2]]))<=0)
                top--;
            Stack[top++]=i;
        }
    }
}

bool cmp1(Line a,Line b)
{
    double x1=a.e.x-a.s.x;
    double y1=a.e.y-a.s.y;
    double x2=b.e.x-b.s.x;
    double y2=b.e.y-b.s.y;
    if(atan2(y1,x1)!=atan2(y2,x2))
        return atan2(y1,x1)<atan2(y2,x2);
    else return x1<y1;
}

bool Seg_inter_line(Line l1,Line l2) //判断直线l1和线段l2是否相交
{
    return sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0;
}

//Point line[maxn];
Line line[maxn];
void rotating_calipers(Line line[],int n)
{
    int cont=0;
    int cur=1;
    int ans=0;
    Point p_ans1,p_ans2;
    int i=0;
    while(1)
    {
        if(cont==n)
            break;
        Point p1,p2,p3,p4;
        Point p_line=Point(line[cont].e.x-line[cont].s.x,line[cont].e.y-line[cont].s.y);
        p1=(p0[i%top]-p0[(i+1)%top]);
        p2=(p0[(cur+1)%top]-p0[(cur)%top]);
        p3=(p0[(i-1+top)%top]-p0[i%top]);
        p4=(p0[(cur)%top]-p0[(cur-1+top)%top]);
        while(sgn(((p0[i%top]-p0[(i+1)%top])^p_line)*((p0[(i-1+top)%top]-p0[i%top])^p_line))>0)
            i=(i+1)%top;
        p_ans1=p0[i%top];
        while(sgn(((p0[(cur+1)%top]-p0[(cur)%top])^p_line)*((p0[(cur)%top]-p0[(cur-1+top)%top])^p_line))>0||cur==i)
        {
             cur=(cur+1)%top;
             //if(cur==i)
                //cur=(cur+1)%top;
        }
        p_ans2=p0[cur%top];
        if(Seg_inter_line(line[cont],Line(p_ans1,p_ans2)))
        {
            strcpy(line[cont].str,"BAD");
            //line[cont].str=="BAD";
        }
        else
        {
            strcpy(line[cont].str,"GOOD");
            //line[cont].str="GOOD";
        }
        cont++;
    }
}

bool cmp3(Line l1,Line l2)
{
    return l1.index<l2.index;
}
int main()
{
    cin>>n;
    int fl=0;
    for(int i=0;i<n;i++)
        scanf("%lf %lf",&p[i].x,&p[i].y);
    if(n<=1)
    {
        double x1,y1,x2,y2;
        while(scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2)!=EOF)
        {
            cout<<"GOOD"<<endl;
        }
    }
    else
    {
        graham();
    for(int i=0;i<top;i++)
    {
        p0[i]=p[Stack[i]];
        //cout<<p0[i].x<<" "<<p0[i].y<<endl;
    }
    double x1,y1,x2,y2;
    int cont=0;
    while(scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2)!=EOF)
    {

        if(y2>y1)
        {
            line[cont]=Line(Point(x1,y1),Point(x2,y2));
        }
        else
        {
            line[cont]=Line(Point(x2,y2),Point(x1,y1));
        }
        line[cont].index=cont;
        cont++;
    }
    sort(line,line+cont,cmp1);
   /*for(int i=0;i<cont;i++)
    {
        cout<<line[i].e.x-line[i].s.x<<endl;
    }*/
    rotating_calipers(line,cont);
    sort(line,line+cont,cmp3);
    for(int i=0;i<cont;i++)
        cout<<line[i].str<<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值