[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

posted @ 2018-05-10 17:12  NewErA  阅读(365)  评论(0编辑  收藏  举报