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

=== ===

这里放传送门

=== ===

题解

可以发现对于一个山坡来说,建造的瞭望塔能看到它的条件就是塔顶连接山谷的那条线的斜率绝对值大于山坡斜率的绝对值,如图:
这里写图片描述
1号和3号山坡是可以被看到的,而2号不可以。
我们可以发现只要把所有的山坡向上做半平面交,瞭望塔的顶点一定要在交集内部。可以发现最小高度的瞭望塔至少有一个顶点在凸多边形或山坡的顶点上,那么为了求出最小的高度,我们可以用 O(n2) 的时间枚举凸多边形和山坡的所有顶点和边来计算高度,也可以用两个指针扫一下。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=1e-12;
const double inf=1e10;
int n;
double ans;
struct Vector{
    double x,y;
    Vector(double X=0,double Y=0){x=X;y=Y;}
    Vector operator + (const Vector &a){return Vector(x+a.x,y+a.y);}
    Vector operator - (const Vector &a){return Vector(x-a.x,y-a.y);}
    double operator * (const Vector &a){return x*a.y-y*a.x;}
    Vector mul(double t){return Vector(x*t,y*t);}
}v[310],tmp,w,ip;
int comp(Vector a,Vector b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
struct Polygon{
    Vector p[500];
    int c;
    Polygon(){c=0;memset(p,0,sizeof(p));}
    void clear(){
        p[1]=Vector(-inf,inf);
        p[2]=Vector(-inf,-inf);
        p[3]=Vector(inf,-inf);
        p[4]=Vector(inf,inf);
        p[5]=p[1];c=4;
    }
    void add(Vector v){p[++c]=v;}
    void Sort(){sort(p+1,p+c+1,comp);}
}poly;
bool Ins(Vector A,Vector B,Vector C,Vector D){
    Vector v=B-A,w=D-C,u=D-A;
    if (fabs(w*v)<eps) return false;
    w=C-A;
    return ((v*w)*(v*u)<-eps);
}
Vector GLI(Vector P,Vector v,Vector Q,Vector w){
    Vector u=P-Q;
    double t=(w*u)/(v*w);
    return P+v.mul(t);
}
Polygon CutPolygon(Polygon poly,Vector A,Vector B){
    Vector C,D,ip;
    Polygon newp=Polygon();
    for (int i=1;i<=poly.c;i++){
        C=poly.p[i];D=poly.p[i+1];
        if ((B-A)*(C-A)>-eps) newp.add(C);
        if (Ins(A,B,C,D)){
            ip=GLI(A,B-A,C,D-C);
            newp.add(ip);
        }
    }
    newp.p[newp.c+1]=newp.p[1];
    return newp;
}
int main()
{
    scanf("%d",&n);poly.clear();
    for (int i=1;i<=n;i++) scanf("%lf",&v[i].x);
    for (int i=1;i<=n;i++) scanf("%lf",&v[i].y);
    for (int i=1;i<n;i++)
      poly=CutPolygon(poly,v[i],v[i+1]);
    v[0]=Vector(-inf,-inf);v[n+1]=Vector(inf,-inf);
    poly.Sort();w=Vector(0,1);ans=1e60;
    for (int i=1,ptr=0;i<=poly.c;i++){
        tmp=poly.p[i];
        while (ptr<=n&&v[ptr].x<tmp.x) ++ptr;
        if (ptr>n) break;//注意不能计算斜线段
        ip=GLI(tmp,w,v[ptr-1],v[ptr]-v[ptr-1]);
        if (ip.x-v[1].x>-eps&&v[n].x-ip.x>-eps)
          ans=min(ans,fabs(ip.y-tmp.y));
    }
    poly.p[0]=Vector(-inf,inf);
    poly.p[poly.c+1]=Vector(inf,inf);
    for (int i=1,ptr=0;i<=n;i++){
        tmp=v[i];
        while (ptr<=poly.c&&poly.p[ptr].x<tmp.x) ++ptr;//找到顶点正下方的线段
        if (ptr>poly.c) break;
        ip=GLI(tmp,w,poly.p[ptr-1],poly.p[ptr]-poly.p[ptr-1]);
        ans=min(ans,fabs(ip.y-tmp.y));
    }
    printf("%.3lf\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值