bzoj1038:[ZJOI2008]瞭望塔(半平面交)

Description

  致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。我们
将H村抽象为一维的轮廓。如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描
述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可
以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长
希望建造的塔高度尽可能小。请你写一个程序,帮助dadzhi村长计算塔的最小高度。

Input

  第一行包含一个整数n,表示轮廓折线的节点数目。接下来第一行n个整数, 为x1 ~ xn. 第三行n个整数,为y1 ~ yn。

Output

  仅包含一个实数,为塔的最小高度,精确到小数点后三位。

Sample Input

【输入样例一】
6
1 2 4 5 6 7
1 2 2 4 2 1
【输入样例二】
4
10 20 49 59
0 10 10 0

Sample Output

【输出样例一】
1.000
【输出样例二】
14.500

HINT

 N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题。


题解:半平面交。

在二维平面上连接前后两点,发现瞭望塔顶端必须在若干直线半平面交区域。

确定了塔顶的范围后,现在要求塔顶到轮廓线上的最近距离。

发现最近距离有两种可能:位于半平面交凸包上的顶点或位于轮廓线顶点处。

枚举求解。

这精度卡得也是可以。。


代码:


#include<bits/stdc++.h>
using namespace std;
const int Maxn=550;
const int INF=0x3f3f3f3f;
int n,tot,L,R,cnt;
struct point{
    double x,y;
    point(double x=0,double y=0):x(x),y(y){}
    friend inline point operator -(const point &a,const point &b){
        return point(b.x-a.x,b.y-a.y);
    }
    friend inline double operator *(const point &a,const point &b){
        return a.x*b.y-a.y*b.x;
    }
}p[Maxn];

struct line{
    point a,b;
    double slope;
    line(){}
    line(point a,point b):a(a),b(b){}
    friend inline bool operator <(const line &a,const line &b){
        if(a.slope!=b.slope)return a.slope<b.slope;
        return (a.a-b.b)*(b.a-b.b)>0;
    }
    friend inline point inter(const line &a,const line &b){
        double k1=(a.b-b.b)*(a.a-b.b);
        double k2=(a.a-b.a)*(a.b-b.a);
        #define t (k1)/(k1+k2)       
        return point(b.b.x+(b.a.x-b.b.x)*t,b.b.y+(b.a.y-b.b.y)*t);
        #undef t 
    }
}l[Maxn];
inline bool judge(line now,line a,line b){
    point q=inter(now,a);
    return (q-b.a)*(q-b.b)>0;
}
inline void hpi(){
    sort(l+1,l+n);
    for(int i=1;i<n;i++){
        if(i==1||l[i].slope!=l[i-1].slope)l[++cnt]=l[i];
    }
    L=1,R=2;
    for(int i=3;i<=cnt;i++){
        while(L<R&&judge(l[i],l[R-1],l[R]))R--;
        l[++R]=l[i];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lf",&p[i].x);
    for(int i=1;i<=n;i++){
        scanf("%lf",&p[i].y);
        if(i!=1){
            l[i-1].a=p[i-1];l[i-1].b=p[i];
            l[i-1].slope=atan2(l[i-1].b.y-l[i-1].a.y,l[i-1].b.x-l[i-1].a.x);
        }
    }
    hpi();
    int pos=L;
    double ans=(double)INF*(double)INF;
    for(int i=1;i<=n;i++){
        while(pos!=R&&inter(l[pos],l[pos+1]).x<p[i].x)pos++;
        point q=inter(line(p[i],point(p[i].x,-1.0)),l[pos]);
        ans=min(ans,q.y-p[i].y);
    }
    pos=1;
    for(int i=L;i<R;i++){
        point q=inter(l[i],l[i+1]);
        if(q.x<p[1].x)continue;
        while(pos<n&&p[pos+1].x<q.x)pos++;
        if(pos==n)break;
        point t=inter(line(p[pos],p[pos+1]),line(q,point(q.x,-1.0)));
        ans=min(ans,q.y-t.y);
    }
    printf("%.3f",ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值