手动博客搬家: 本文发表于20181208 14:39:01, 原地址https://blog.csdn.net/suncongbo/article/details/84891710
哇它居然显示出图片来了……感动啊
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4739http://uoj.ac/problem/277
不难得出一个结论: 两圆之间最短路一定沿着圆的公切线走。然后得到如下算法:每两个圆之间作公切线(4条),如果一条公切线不穿过其他圆,就把这两个点(图论中的点)之间连上边,边权为切线长;同一圆上相邻两点连边,边权为圆上距离,然后S,T分别向每个圆作切线如果不和其他圆相交就连边。这样的话点数、边数是\(O(n^2)\)级别的,使用Dijkstra算法求最短路即可。时间复杂度瓶颈在于对于一条边枚举每一个圆去check是否相交,总时间复杂度\(O(n^3)\). 奇迹般地能跑过。
下面重点讲解一下我是如何求两圆公切线以及点和圆的切线的:
先来说两圆公切线:因为两圆外离所以一定有\(4\)条公切线,\(2\)内\(2\)外。
考虑先把圆按半径从大到小排序,现在要从半径大的圆向半径小的圆作\(4\)条切线。
设两圆圆心(已知)分别为\(O_1, O_2\), 圆心的直线距离\(O_1O_2=d\), 半径分别为\(r_1r_2\), 一条外公切线为\(AB\)(未知)。
过\(O_2\)作\(O_2E\perp AO_1\)于\(D\), 则\(AEO_2B\)为矩形。\(AE=BO_2=r_2\), \(EO_1=AO_1-AE=r_1-r_2\), 又\(O_1E\perp O_2E\), 可用勾股定理算出切线长\(AB=EO_2=\sqrt{d^2-(r_1-r_2)^2}\). 同时有\(\cos \angle EO_1O_2=\frac{r_1-r_2}{d}\), 可利用acos
函数算出\(\angle EO_1O_2\)的值。然后求出向量\(\vec {OA}=\frac{r_1}{d}\vec v\)即可,其中\(\vec v\)为\(\vec {O_1O_2}\)逆时针旋转\(\angle EO_1O_2\)的向量。
另外一条外公切线同理,把旋转度数取反即可。
代码如下:
Line tmp; double d = EuclidDist(a[i].o,a[j].o); Vector v; double ang; Point p1,p2;
//Out 1
ang = acos((a[i].r-a[j].r)/d); v = rotate(Vector(a[j].