[bzoj1020][SHOI2008]安全的航线flight【迭代】【计算几何】

【题目链接】
  http://www.lydsy.com/JudgeOnline/problem.php?id=1020
【题解】
  首先有一个朴素的想法就是二分,然后判断带圆弧的多边形是否能完全覆盖直线,是不是很简单?
  但我不会 QAQ
  考虑暴力枚举每一个点(隔0.005),求出它到最近陆地的距离,取最大值。
  这样做编程复杂度会下降很多,但会 TLE。
  观察后发现,许多状态是多余的,于是想到剪枝,具体的方法是:
    1.将每一条线段加入队列中。
    2.区出队收线段,找出线段两端的最近点,二分找出距离这两个点最远的在线段上点,设为 mid,若它到这两个点的最近距离 当前答案,就将整个线段删除。
    tips:找 mid 时,它要么在线段最左边,要么在最右边,要么在垂直平分线上,所以二分垂直平分线(直接求也行),然后与左右比较。
    3.用 mid 更新答案。
    4.加入左端点到 midmid 到右端点的线段。(满足长度≥0.005)
    5.重复2-4直到队列为空。
  这种做法能顺利通过此题,复杂度 O(???)
  还是挺好(nan)写的

/* --------------
    user Vanisher
    problem bzoj-1020 
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     1e9
# define    eps     1e-4
# define    anseps  1e-3
# define    N       110
# define    T       5000000
using namespace std;
int num[N],n,m,k;
struct point{
    double x,y;
}mp[N][N],rd[N];
struct seg{
    point a,b;
}l[N*N],q[T];
double ans;
point operator +(point x, point y){return (point){x.x+y.x,x.y+y.y};}
point operator -(point x, point y){return (point){x.x-y.x,x.y-y.y};}
point operator *(point x, double y){return (point){x.x*y,x.y*y};}
point operator /(point x, double y){return (point){x.x/y,x.y/y};}
double sqr(double x){return x*x;}
double dis(point x, point y){
    return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));
}
double dot(point x, point y){
    return x.x*y.x+x.y*y.y;
}
double cross(point x, point y){
    return x.x*y.y-x.y*y.x;
}
double dis_PtoS(point x, seg y){
    if (dot(x-y.a,y.b-y.a)<0) return dis(x,y.a);
    if (dot(x-y.b,y.a-y.b)<0) return dis(x,y.b);
    return fabs(cross(y.a-x,y.b-x))/dis(y.a,y.b);
}
point PtoS(point x, seg y){
    if (dot(x-y.a,y.b-y.a)<0) return y.a;
    if (dot(x-y.b,y.a-y.b)<0) return y.b;
    double d=dis_PtoS(x,y), d1=sqrt(sqr(dis(y.a,x))-sqr(d));
    return y.a+(y.b-y.a)*(d1/dis(y.a,y.b));
}
bool PonS(point x, seg y){
    return fabs(cross(x-y.a,x-y.b))<eps&&dot(x-y.a,x-y.b)<eps;
}
bool ScrossS(seg a, seg b){
    return cross(a.a-b.a,b.b-b.a)*cross(a.b-b.a,b.b-b.a)<eps&&cross(b.a-a.a,a.b-a.a)*cross(b.b-a.a,a.b-a.a)<eps;
}
bool in(point x, int id){
    for (int i=1; i<=num[id]; i++)
        if (PonS(x,(seg){mp[id][i],mp[id][i+1]})==true) 
            return true;
    point y={10001,x.y+0.1}; int cnt=0;
    for (int i=1; i<=num[id]; i++)
        if (ScrossS((seg){x,y},(seg){mp[id][i],mp[id][i+1]})==true) cnt++;
    return cnt%2;
}

int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}

bool check(point x){
    bool now=false;
    for (int i=1; i<=m; i++){
        now=now|in(x,i);
    }
    return now;
}
point findnear(point x){
    if (check(x)) return x;
    point mn={inf,inf};
    for (int i=1; i<=k; i++){
        point now=PtoS(x,l[i]);
        if (dis(mn,x)>dis(now,x)) mn=now;
    }
    return mn;
} 
void checkans(point x){
    point y=findnear(x);
    ans=max(ans,dis(x,y));
}
int main(){
    m=read(), n=read();
    for (int i=1; i<=n; i++)
        rd[i].x=read(), rd[i].y=read(); 
    for (int i=1; i<=m; i++){
        num[i]=read();
        for (int j=1; j<=num[i]; j++)
            mp[i][j].x=read(), mp[i][j].y=read();
        mp[i][num[i]+1]=mp[i][1];
        for (int j=1; j<num[i]; j++)
            l[++k].a=mp[i][j], l[k].b=mp[i][j+1];
        l[++k].a=mp[i][num[i]], l[k].b=mp[i][1];
    }
    int pl=1, pr=0;
    for (int i=1; i<n; i++)
        q[++pr].a=rd[i],q[pr].b=rd[i+1];
    while (pl<=pr){
        seg now=q[pl++];
        point lp=findnear(now.a), rp=findnear(now.b), l=now.a, r=now.b, mid;
        while (dis(l,r)>anseps){
            mid=(l+r)/2;
            if (dis(mid,lp)>dis(mid,rp)) 
                r=mid; else l=mid;
        }
        checkans(now.a); checkans(now.b); checkans(mid);
        if (dis(mid,lp)<ans+eps) continue; 
        if (dis(now.a,mid)>anseps) q[++pr].a=now.a, q[pr].b=mid;
        if (dis(mid,now.b)>anseps) q[++pr].a=mid, q[pr].b=now.b;
    }
    printf("%.2lf\n",ans);
    return 0;
}
posted @ 2018-02-25 18:37  Vanisher  阅读(142)  评论(0编辑  收藏  举报