ZOJ 2967——Colorful Rainbows

来源:互联网 发布:热处理工艺模拟软件 编辑:程序博客网 时间:2024/06/05 14:23

题意:有最多5000条线,要求出这些线里面能看到几条。能看到的线是指这条线在一个区间内比其他任何线的y坐标都要大。
思路:根据题目意思,可以看出要求的是在直角坐标系里面最“上面”的那几条线。能看到的线应该组成了一个两边高,中间低的凹的形状。知道了最后要求的大致形状以后,差不多可以想到按从左往右的方式来判断直线是否可见。
先不考虑平行直线,斜率最小的跟斜率最大的直线是一定可见的。从斜率最小的开始考虑。将直线按照斜率从小到大排序。第一条直线即斜率最小的直线,一定是可见的。加入第二条直线,这时因为只有两条直线,所以俩都可见。再加入第三条。第三条的斜率比第二个大,它不可能遮挡住第一条直线,因此它最多只能挡住第二条直线。如果第三条直线与第二条直线的交点的横坐标x1,比第二条直线与第一条直线交点的横坐标x2小或等于,那么第二条直线一定被遮住了(画画图就看出来了)。如果第二条直线没有被遮住,那么这时就可见三条直线。这时,第二条直线有两个交点,将横坐标小的记为p1,将横坐标大的记为p2。
往后每加入一条直线都如此。这样,每一条可见的直线都会记录两个坐标,代表这条直线可见的区间(第一条直线只有与第二条直线一个交点,可以将第一条直线与所有直线中交点最小的给求出来作为它的p1,这样就将第一条直线的处理一般化了)。新加入一条直线以后,如果它会遮挡住直线l,那么在前面判断可见的直线中,斜率比l大的全部都会被遮住。
在判断的过程中,可见的直线相当于一个栈,栈中元素斜率依次增加。每加入一条直线,在里面找不能被挡住的最后一个直线,这条直线以后的所有都看不到了。
我用了两种方法实现,一种是栈顶元素一个一个弹出,另一种是用二分找到最后一个能看到的直线。
代码:

#include<iostream>#include<cstring>#include<vector>#include<algorithm>#include<cstdio>#include<map>#include<cmath>#include<assert.h>#include<iomanip>#include<assert.h>using namespace std;typedef long long ll;int dcmp(double x){    if(fabs(x)<1e-9)return 0;    return x<0?-1:1;}struct Point{    double x,y;    Point(double _x=0,double _y=0):x(_x),y(_y){}    bool operator < (const Point &a) const {        return x<a.x;    }    bool operator > (const Point &a) const {        return x>a.x;    }    bool operator <= (const Point &a) const {        return dcmp(x-a.x)<=0;    }    bool operator >= (const Point &a) const {        return dcmp(x-a.x)>=0;    }};struct Line{    double a,b;    Point p1;    Point p2;    bool operator < (const Line &l) const{        if(dcmp(a-l.a)!=0)return a<l.a;        return b>l.b;    }}line[5005];Point crosspoint(Line l1,Line l2){    double x = (l2.b-l1.b)/(l1.a-l2.a);    return Point(x,l1.a*x+l1.b);}int main(){//    freopen("data.txt","r",stdin);    int T;    scanf("%d",&T);    while(T--){        int n;        scanf("%d",&n);        for(int i=0;i<n;++i){            scanf("%lf%lf",&line[i].a,&line[i].b);        }        sort(line,line+n);        line[0].p1=crosspoint(line[1],line[0]);        for(int i=1;i<n;++i){            Point c = crosspoint(line[0],line[i]);            line[0].p1=min(line[0].p1,c);        }        line[0].p1 = Point(line[0].p1.x-1,(line[0].p1.x-1)*line[0].a+line[0].b);//第一条直线的预处理,给它的p1赋值。横坐标减一是为了防止所有点都与第一条直线相交于一点的情况。        int last=0;        for(int i=1;i<n;++i){            if(dcmp(line[i].a-line[i-1].a)==0){                continue;            }            assert(last>=0);            line[last].p2 = crosspoint(line[last],line[i]);            if(line[last].p2<=line[last].p1){                last--;            }//最后的可见的直线的p2都没有求,新加入直线的时候先把它求出来。            ///////////////////+++++++++++++++++++++++//            while(last>=0){//                Point cp = crosspoint(line[last],line[i]);//                if(cp<=line[last].p1){//                    last--;//                } else {//                    line[i].p1 = cp;//                    line[last].p2 = cp;break;//                }//            }//            line[++last]=line[i];//            continue;            ////////////////////+++++++++++++++++++++++            int l=0;            int r=last;            while(l<=r){                int mid = (l+r)>>1;                Point cp = crosspoint(line[i],line[mid]);                if(line[mid].p1>line[mid].p2)swap(line[mid].p1,line[mid].p2);                if(cp>line[mid].p1&&cp<=line[mid].p2){                    l=r=mid;line[mid].p2=cp;line[i].p1=cp;last = mid;break;                } else if(cp>line[mid].p2){                    l=mid+1;                } else if(cp<=line[mid].p1){                    r=mid-1;                } else assert(0);            }            line[last+1]=line[i];            last++;        }        printf("%d\n",last+1);    }    return 0;}
0 0
原创粉丝点击