BZOJ 1020 [安全的航线 flight]

题面

题意

  给出一些简单多边形,再给出一条折线,问折线上的点距离所有多边形最短距离的最大值是多少。

题解

  方法来自莫涛的论文《迭代思想的应用》。具体过程如下:

  • 不妨令线段 \(Seg\) 距离所有多边形最短距离的最大值为 \(dist(Seg)\),找到 \(dist(Seg)\) 尽可能紧的上下界 \(f(Seg),g(Seg)\),也就是 \(g(Seg) \le dist(Seg) \le f(Seg)\),要求在 \(Seg\) 的长度趋近于 \(0\)\(f(Seg)=dist(Seg)=g(Seg)\)。将答案 \(ans\) 初始化为 \(+\infty\)

  • 将折线在多边形内部的部分舍弃,得到一系列位于多边形外的线段,将其放入队列。

  • 对于队列首的线段 \(Seg\),如果 \(f(Seg) \le ans\) 就舍弃。否则用 \(g(Seg)\) 更新 \(ans\),也就是 \(ans=\max(ans,g(Seg))\),然后将 \(Seg\) 按中点分成两段加到队列末端。重复此操作直到队列为空。

  • 此时的 \(ans\) 就是最终答案。

  下面将 \(g(Seg)\) 设置成了中点到多边形最短距离的最大值,\(f(Seg)\) 设置成了距离 \(Seg\) 一端最近的线段到 \(Seg\) 两端距离的较大值。迭代算法很难给出精确的复杂度,换句话说就是复杂度玄学,但跑得很快。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
typedef double ld;
inline ld _abs(ld n){ return n<0?-n:n; }
const ld pi=acos(-1),ee=1e-9,inf=1e18;
struct cplx{
    ld x,y;
    cplx(){}
    cplx(ld x,ld y):x(x),y(y){}
    ld abs(){ return sqrt(x*x+y*y); }
};
cplx operator + (const cplx &a,const cplx &b){ return cplx(a.x+b.x,a.y+b.y); }
cplx operator - (const cplx &a,const cplx &b){ return cplx(a.x-b.x,a.y-b.y); }
cplx operator * (const cplx &a,const ld &b){ return cplx(a.x*b,a.y*b); }
cplx operator / (const cplx &a,const ld &b){ return cplx(a.x/b,a.y/b); }
ld dot(const cplx &a,const cplx &b){ return a.x*b.x+a.y*b.y; }
ld det(const cplx &a,const cplx &b){ return a.x*b.y-a.y*b.x; }
ld abs(const cplx &a){ return sqrt(a.x*a.x+a.y*a.y); }
int sign(ld n){ return (n>ee)-(n<-ee); }
struct seg{
    cplx a,b;
    seg(){}
    seg(cplx a,cplx b):a(a),b(b){}
    cplx mid(){ return cplx((a.x+b.x)/2,(a.y+b.y)/2); }
};
vector<seg> border;
queue<seg> que,tmp;
void split(const seg &a,const seg &b,queue<seg> &que){
    if (sign(det(a.a-a.b,b.a-b.b))==0){
        que.push(b); return;
    }
    ld t1=det(b.a-b.b,a.a-b.b),t2=det(b.a-b.b,b.b-a.b);
    cplx cp=(a.a*t2+a.b*t1)/(t1+t2);
    if (sign((b.a-b.b).abs()-(b.a-cp).abs()-(b.b-cp).abs())){
        que.push(b); return;
    }
    que.push(seg(b.a,cp));
    que.push(seg(cp,b.b));
}
ld dist(const cplx &a,seg b){
    b.a=b.a-a; b.b=b.b-a;
    if (dot(b.a,b.a-b.b)<0) return b.a.abs();
    if (dot(b.b,b.b-b.a)<0) return b.b.abs();
    return fabs(det(b.a,b.b))/(b.a-b.b).abs();
}
bool inside(const cplx &cp,const vector<seg> &land){
    double ans=0,a,b;
    seg sg;
    for (int i=0;i<land.size();i++){
        sg=land[i];
        if (!sign(dist(cp,sg))) return true;
        a=atan2((sg.a-cp).x,(sg.a-cp).y);
        b=atan2((sg.b-cp).x,(sg.b-cp).y);
        while (a<b-pi) a+=pi;
        while (b<a-pi) b+=pi;
        ans+=b-a;
    }
    return sign(ans);
}
double esti(const seg &a){
    double dis=inf;
    seg cur,sg;
    for (int i=0;i<border.size();i++)
        if (dist(a.a,sg=border[i])<dis)
            dis=dist(a.a,cur=sg);
    return max(dis,dist(a.b,cur));
}
double calc(const cplx &a){
    double ans=inf;
    for (int i=0;i<border.size();i++)
        ans=min(ans,dist(a,border[i]));
    return ans;
}
int main(){
    int i,j,n,m,t;
    cplx a,b;
    ld ans;
    seg line,sg;
    vector<seg> land;
    scanf("%d%d",&m,&n);
    scanf("%lf%lf",&a.x,&a.y);
    for (i=1;i<n;i++){
        scanf("%lf%lf",&b.x,&b.y);
        que.push(seg(a,b));
        a=b;
    }
    for (i=0;i<m;i++){
        scanf("%d",&t);
        scanf("%lf%lf",&a.x,&a.y);
        for (j=1;j<t;j++){
            scanf("%lf%lf",&b.x,&b.y);
            land.push_back(seg(a,b));
            a=b;
        }
        land.push_back(seg(a,land[0].a));
        for (int i=0;i<land.size();i++){
            sg=land[i];
            while (!que.empty()){
                line=que.front(); que.pop();
                split(sg,line,tmp);
            }
            swap(que,tmp);
        }
        while (!que.empty()){
            line=que.front(); que.pop();
            if (!inside(line.mid(),land)) tmp.push(line);
        }
        swap(que,tmp);
        border.insert(border.end(),land.begin(),land.end());
        land.clear();
    }
    ans=0;
    while (!que.empty()){
        line=que.front(); que.pop();
        if (esti(line)<ans+ee) continue;
        ans=max(ans,calc(line.mid()));
        que.push(seg(line.a,line.mid()));
        que.push(seg(line.mid(),line.b));
    }
    printf("%.2f\n",(double)ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Kilo-5723/p/12714423.html
今日推荐