bzoj 1038 瞭望塔 半平面交 + 最小值 枚举

来源:互联网 发布:7u分享网络注册 编辑:程序博客网 时间:2024/05/17 21:52

题目来源:

http://61.187.179.132/JudgeOnline/problem.php?id=1038

给出n个点,从 x值较小到较大给出, 连成的一条折线, 求从这折线上一点放一个垂直的杆,在杆顶能看到整个折线。 求杆的最小长度。

分析: 显然杆的最小摆放位置在 折线顶点 或者 半平面交的交点处。

代码如下:

const double EPS = 1e-10 ;const int Max_N =  305 ;const double inf = 1e10 ; // 这个 开小了 1e9 都WA了好几次double add(double a, double b){    return (fabs(a + b) < EPS * (fabs(a) + fabs(b)) ) ?  0 : (a + b );}struct Point {    double x, y;    Point(){}    Point(double x, double y):x(x) , y(y){}    Point operator - (Point a){        return Point(add(x , -a.x) , add(y , -a.y)) ;    }    Point operator + (Point a){        return Point(add(x , a.x) , add(y , a.y)) ;    }    double operator ^(Point a){        return add(x * a.y ,  - y * a.x) ;    }    Point operator * (double d){        return Point(x * d , y * d) ;    }    void write(){        printf("%lf %lf\n" , x , y) ;    }};struct Line{  //有向直线    Point st, ed;    Line(){}    Line(Point s, Point e){        st = s ;        ed = e ;    }    bool onRight(Point a){ //点a在直线向量的右边        return ((ed - st)^(a - st)) < 0 ;    }    bool parallel(Line l){        return ((ed -st)^(l.ed - l.st)) == 0 ;    }    Point Crossnode(Line l){ //两直线的交点        double t = (l.ed - l.st) ^(l.st - st) ;        double t1 = (l.ed - l.st)^(ed - st) ;        return st + (ed - st)*(t / t1) ;    }    bool onseg(Point a){        return ((a.x - st.x)*(a.x - ed.x) <EPS) && ((a.y - st.y)*(a.y - ed.y) <EPS) ;    }    double jijiao(){        return atan2(ed.y - st.y , ed.x - st.x) ;    }    void write(){        printf("%lf %lf %lf %lf\n" , st.x ,st.y ,ed.x ,ed.y) ;    }};//排序函数 [优先顺序:1极角  2. 前面的直线在后面的左边 ]bool operator < (Line l, Line r){    double lp = l.jijiao() ;    double rp = r.jijiao() ;    if( fabs(lp - rp) > EPS)        return lp < rp ;    return ((l.st - r.st)^(r.ed - r.st)) < -EPS ;}//用于计算的双端队列Line dequeue[Max_N] ;Point pt[Max_N] ;int halfPanelCross(Line line[] , int ln){    int i, tn;    sort(line, line  + ln ) ;// 平面在向量左边 的筛选    for(i = tn = 1 ; i < ln ; i++){// 处理极角相同的,选择向量方向最左边的        if(fabs(line[i].jijiao() - line[i -1].jijiao())  > EPS)            line[tn++] = line[i] ;    }    ln = tn ;    int bot = 0 , top = 1 ;    dequeue[0] = line[0] ;    dequeue[1] = line[1] ;    for(i = 2 ; i < ln ; i++){        if(dequeue[top].parallel(dequeue[top - 1])|| dequeue[bot].parallel(dequeue[bot + 1]))            return 0 ;        while(bot < top &&              line[i].onRight(dequeue[top].Crossnode(dequeue[top - 1])))            top -- ;        while(bot < top &&             line[i].onRight(dequeue[bot].Crossnode(dequeue[bot + 1])))            bot ++ ;        dequeue[++ top] = line[i] ;    }    while(bot < top &&          dequeue[bot].onRight(dequeue[top].Crossnode(dequeue[top -1])))        top -- ;    while(bot < top &&          dequeue[top].onRight(dequeue[bot].Crossnode(dequeue[bot + 1])))        bot ++ ;    if(top <= bot + 1) return 0 ;  // 若队列为空, 则空集    int n = 0 ;    //计算交点(注意不同直线形成的交点可能重合) 半平面交是凸多边形    for(i = bot ; i < top ; i++)        pt[n ++] = dequeue[i].Crossnode(dequeue[i + 1]) ;    if(bot < top +1)        pt[n ++] = dequeue[bot].Crossnode(dequeue[top]) ;    return n ;}Line List[Max_N] ;Point p[Max_N] ;int n;void go(){    int i , j;    Point s, t ;    Line tmp ;    double ans = inf;    i = n  -1 ;    List[i ++] = Line(Point(-inf ,-inf) , Point(inf , -inf)) ;    List[i ++] = Line(Point(inf ,-inf) , Point(inf , inf)) ;    List[i ++] = Line(Point(inf ,inf) , Point(-inf , inf)) ;    List[i ++] = Line(Point(-inf ,inf) , Point(-inf , -inf)) ;    int ttn = i ;    int tn = halfPanelCross(List , ttn) ;    for(i = 0 ; i < n ; i++){        s = p[i] ;        t.x = s.x  , t.y = 1000 ;        for( j = 0 ; j < tn ; j++){            tmp = Line(pt[j] , pt[(j + 1) % tn]) ;            Point cross = Line(s ,t).Crossnode(tmp) ;            if(tmp.onseg(cross))                ans = min(ans , cross.y - s.y) ;        }    }    for(i = 0 ; i < tn ; i++){        s = pt[i] ;        t.x = s.x , t.y = 1000 ;        for(j = 0 ; j < n - 1 ; j++){            tmp = Line(p[j] , p[j + 1]) ;            Point cross = Line(s,t).Crossnode(tmp) ;            if(tmp.onseg(cross))                ans = min(ans , s.y - cross.y) ;        }    }    printf("%.3lf\n" , ans + EPS) ;}int main(){    int i ;    while(scanf("%d" , &n) != EOF){        for(i = 0 ; i < n ; i++)            scanf("%lf" , &p[i].x) ;        for(i = 0 ; i < n ; i++)            scanf("%lf" , &p[i].y) ;        for(i = 0 ; i < n-1 ; i++){                List[i] = Line(p[i] , p[i + 1]) ;        }        go() ;    }}

 

0 0
原创粉丝点击