• [Codeforces 8D] Two Friends


    Brief Introduction:

    有两人a、b,他们都在A点,a经过B点到C点,而b直接到C点。a走过的距离不超过la,b走过距离不超过lb,询问他们可能经过最长的公共距离。

    Algorithm1:

    我们首先可以发现一个公共距离是否可行是具有单调性的

    从而可以考虑使用二分

    于是我们将问题转化为已知三个圆,询问着三个圆是否有公共部分

    对于这类问题,我们每次求出三个圆中两两的交点,判断其是否在第三个圆内即可

    Algorithm2:

    我们假设公共路径在AD上,D在线段BC上,我们可以发现在D从B移动到C时,最长公共距离的长度是凸性函数

    我们由此想到三分法

    而对于每一个特定的D点,其公共距离的长度同算法1一样具有单调性,使用二分法即可

    Code1:

    #include <bits/stdc++.h>
    
    using namespace std;
    const double eps=1e-12;
    
    #define point complex<double>
    
    point a,b,c;
    double AB,BC,AC,ta,tb;
    
    void Read(point &k)
    {
        double x,y;cin >> x >> y;
        k=point(x,y);
    }
    
    bool intersect(point a,double Ra,point b,double Rb,point c,double Rc)
    {
        if(abs(a-b)-(Ra+Rb)>eps) return false;
        if(abs(a-c)-Ra<eps && abs(b-c)-Rb<eps) return true;
        if(abs(a-b)+Ra-Rb<-eps || abs(a-b)+Rb-Ra<-eps) return false;
        
        b-=a;c-=a;
        point i=point(b.real()/abs(b),b.imag()/abs(b));  //对原图进行线性变换,求出新的基向量
        b/=i;c/=i;
        
        double x=(Ra*Ra-Rb*Rb+abs(b)*abs(b))/(2*abs(b)); //用勾股定理求交点
        
        double h=sqrt(max(Ra*Ra-x*x,0.0));
        
        if(abs(point(x,h)-c)-Rc<eps || abs(point(x,-h)-c)-Rc<eps) return true;  //对上下两个交点都进行判断
        return false;
    }
    
    bool eval(point a,double Ra,point b,double Rb,point c,double Rc) //查看两两的交点是否在第三圆内
    {
        if(Ra<eps || Rb<eps || Rc<eps) return false;
        if(intersect(a,Ra,b,Rb,c,Rc)) return true;
        if(intersect(a,Ra,c,Rc,b,Rb)) return true;
        if(intersect(b,Rb,c,Rc,a,Ra)) return true;
        return false;
    }
    
    int main()
    {
        cout.setf(ios::fixed);
        cout.precision(20);
        
        cin >> ta >> tb;
        Read(a);Read(c);Read(b);
        AC=abs(a-c);AB=abs(a-b);BC=abs(b-c);
        
        ta+=AB+BC;tb+=AC;
        if(tb-(AB+BC)>-eps)
            return cout << min(tb,ta),0;
        
        double l=0,r=min(ta,tb);
        while(fabs(r-l)>eps) //对答案二分
        {
            double m=(r+l)*.5;
            if(eval(a,m,b,ta-BC-m,c,tb-m)) l=m;
            else r=m;
        }
        cout << (r+l)*.5;
        return 0;
    }

    Code2:

    #include <bits/stdc++.h>
    
    using namespace std;
    const double eps=1e-13;
    
    struct Point
    {
        double x,y;
        Point(){}
        Point(double a,double b){x=a,y=b;}
        void input(){cin >> x >> y;}
        double dist(Point&a){return hypot(x-a.x,y-a.y);}
    };
    
    double ta,tb,w,AB,AC,BC,AU,UB,UC,lm,rm;
    Point A,B,C;
    
    double eval(double k)
    {
        Point U=Point(k*B.x+(1-k)*C.x,k*B.y+(1-k)*C.y);
        AU=A.dist(U),UB=U.dist(B),UC=U.dist(C);
        if(AU+UB<ta && AU+UC<tb)
            return min(ta-UB,tb-UC);
        
        double l=0,r=1;
        while(fabs(l-r)>eps) //二分
        {
            w=(l+r)*0.5;
            Point V=Point(w*U.x+(1-w)*A.x,w*U.y+(1-w)*A.y);
            if(w*AU+V.dist(B)<ta && w*AU+V.dist(C)<tb) l=w;
            else r=w;
        }
        return (l+r)*0.5*AU;
    }
    
    int main()
    {
        cout.setf(ios::fixed);
        cout.precision(15);
        cin >> ta >> tb;
        A.input();C.input();B.input();
        AB=A.dist(B),AC=A.dist(C),BC=B.dist(C);
        
        ta+=AB+1e-12;tb+=AC+1e-12; //先加上eps,解决精度问题
        
        if(tb>AB+BC) 
        {
            cout << min(tb,ta+BC);
            return 0;
        }
        
        double l=0,r=1;
        while(fabs(l-r)>eps) //三分
        {
            lm=(2*l+r)/3,rm=(2*r+l)/3;
            if(eval(lm)>eval(rm)) r=rm;
            else l=lm;
        }
        cout << eval((l+r)*0.5);
        return 0;
    }

    Review:

    1、使用complex类解决计算几何问题

          使用abs、hypot函数

    2、线性变换

          首先确定原点A,求出其它坐标与原点的相对位置

          其次求出单位向量P(Xb/abs,Yb/abs),将AB作为X轴

          最后使用复数除法,将其它向量除去单位向量,确定新的坐标

    3、判断三圆是否有公共部分:

          两两使用勾股定理判交点是否在第三个圆内

    4、在无法确定某个距离时,寻找其中的凸性或单调性,用三分或二分解决

          计算几何的常用策略

    5、精度问题:一般选择超出答案要求的2到3位,过少会WA,过多会TLE

  • 相关阅读:
    常用的文件查看命令
    Linux常用快捷按键
    寒冬储粮
    创建型模式:抽象工厂
    创建型模式:工厂方法
    创建型模式:单例模式
    开闭原则
    迪米特法则
    接口隔离原则
    依赖倒置原则
  • 原文地址:https://www.cnblogs.com/newera/p/9016952.html
Copyright © 2020-2023  润新知