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