AT_joisc2016_k 题解

AT_joisc2018_b

传送门

题意

有一个以原点为中心的正方形,有 \(n(n\le 100)\) 条不在正方形内部的线段,你需要画一些不在正方形内部的线段,使得这些线段可以把正方形围起来,要求最小化你画的线段的长度和。

思路

我们需要画出一条闭合折线,并且能够把正方形包围。

考虑我们一定是把已有线段用新的线段连起来,或者是让已有线段和正方形边界连起来。因为线段不能在正方形内,我们把两个线段连接起来如果要用正方形边界,就一定会覆盖一个拐角,就是说一定不会只用正方形一条边界的一部分,因此我们可以把只把正方形的四个角当成线段,这样我们就只需要考虑连接两个线段的贡献了。

对于每一对线段,我们求出连接这两个线段最小的代价和这条线段。注意如果这条线段和正方形有交,我们就不能选这条线段,而且我们用其他方式直接连接这两个线段一定没有通过正方形边界更优,因此不用考虑。现在问题就变成了找最小的环使得包含这个正方形。

对于判包含,我们可以从正方形中引出一条射线,如果这条射线经过了折线奇数次,就说明被包含了正方形。

于是对于每一对线段,我们求出连接这两个点的线段经过了射线多少次,这样我们的问题就是求一个经过射线次数为奇数的最小环,可以用 Floyd 解决。

复杂度 \(O(n^3)\)

点击查看代码
mt19937 nsc(time(0));
const int N=105;
const double eps=1e-8,inf=1e18,pi=acos(-1);
int n;
double S,SS;
inline int cmp(double a,double b){
    if(fabs(a-b)<eps)return 0;
    return a>b?1:-1;   
}
struct point{
    double x,y;
    point() {}
    point (double xx,double yy){x=xx,y=yy;}
    point operator +(const point &a)const{return point(x+a.x,y+a.y);}
    point operator -(const point &a)const{return point(x-a.x,y-a.y);}
    point operator *(const double &a)const{return point(x*a,y*a);}
    point operator /(const double &a)const{return point(x/a,y/a);}
    double operator *(const point &a)const{return x*a.y-y*a.x;}
    double operator /(const point &a)const{return x*a.x+y*a.y;}
    double length(){return sqrt(x*x+y*y);}
};
struct segment{
    point a,b;
    segment() {}
    segment(point aa,point bb){a=aa,b=bb;}
    inline double length(){return (b-a).length();}
    bool operator <(const segment &x)const{return 0;}
}a[N],line;
double f[N][N][2];
inline bool checkcap(double l1,double r1,double l2,double r2){
    if(r1<l1)swap(l1,r1);
    if(r2<l2)swap(l2,r2);
    if(!~cmp(r1,l2))return 0;
    if(!~cmp(r2,l1))return 0;
    return 1;
}
inline bool cross(segment a,segment b){
    if(!checkcap(a.a.x,a.b.x,b.a.x,b.b.x))return 0;
    if(!checkcap(a.a.y,a.b.y,b.a.y,b.b.y))return 0;
    int flag1=cmp((a.b-a.a)*(b.a-a.a),0),flag2=cmp((a.b-a.a)*(b.b-a.a),0);
    if(abs(flag1-flag2)!=2)return 0;
    flag1=cmp((b.b-b.a)*(a.a-b.a),0),flag2=cmp((b.b-b.a)*(a.b-b.a),0);
    if(abs(flag1-flag2)!=2)return 0;
    return 1;
}
inline pair<double,segment> geth(point a,segment x){
    point p=x.b-x.a;
    double len=((a-x.a)/(x.b-x.a))/p.length();
    if(cmp(len,p.length())>0)return make_pair(inf,x);
    if(cmp(len,0)<0)return make_pair(inf,x);
    point h=x.a+p*(len/p.length());
    segment ans=segment(a,h);
    return make_pair(ans.length(),ans);
}
inline void solve(segment a,segment b,int i,int j){
    pair<double,segment> ans=min(make_pair(segment(a.a,b.a).length(),segment(a.a,b.a)),make_pair(segment(a.a,b.b).length(),segment(a.a,b.b)));
    ans=min({ans,make_pair(segment(a.b,b.a).length(),segment(a.b,b.a)),make_pair(segment(a.b,b.b).length(),segment(a.b,b.b))});
    ans=min({ans,geth(a.a,b),geth(a.b,b)});
    pair<double,segment> minn=min(geth(b.a,a),geth(b.b,a));
    swap(minn.second.a,minn.second.b);
    ans=min(ans,minn);
    if(cross(ans.second,segment(point(-SS,SS),point(SS,SS))))return;
    if(cross(ans.second,segment(point(SS,-SS),point(SS,SS))))return;
    if(cross(ans.second,segment(point(-SS,-SS),point(SS,-SS))))return;
    if(cross(ans.second,segment(point(-SS,-SS),point(-SS,SS))))return;
    if(cross(ans.second,segment(point(-SS,-SS),point(SS,SS))))return;
    if(cross(ans.second,segment(point(-SS,SS),point(SS,-SS))))return;
    bool flag=cross(segment(a.a,ans.second.a),line)^cross(ans.second,line)^cross(segment(ans.second.b,b.a),line);
    f[i][j][flag]=f[j][i][flag]=ans.first;
}
int main(){
    cin>>n>>S;
    for(int i=1;i<=n;i++){
        cin>>a[i].a.x>>a[i].a.y>>a[i].b.x>>a[i].b.y;
    }
    line=segment(point(0.0,0.0),point(10000.0,10000.0*((nsc()%10000)/10000.0*pi/2)));
    SS=S-eps*100;
    a[++n]=segment(point(S,S+eps),point(S+eps,S));
    a[++n]=segment(point(-S-eps,S),point(-S,S+eps));
    a[++n]=segment(point(-S-eps,-S),point(-S,-S-eps));
    a[++n]=segment(point(S,-S-eps),point(S+eps,-S));
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j][0]=f[i][j][1]=inf;
    for(int i=1;i<=n;i++)f[i][i][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            solve(a[i],a[j],i,j);
        }
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                for(int p=0;p<2;p++){
                    for(int q=0;q<2;q++){
                        f[i][j][p^q]=min(f[i][j][p^q],f[i][k][p]+f[k][j][q]);
                    }
                }
            }
        }
    }
    double ans=inf;
    for(int i=1;i<=n;i++)ans=min(ans,f[i][i][1]);
    cout<<fixed<<setprecision(10)<<ans<<endl;
    return 0;
}
posted @ 2024-01-03 16:14  Xttttr  阅读(11)  评论(0编辑  收藏  举报