UOJ題目傳送門c++
顯然最優的路徑只會通過若干條兩個圓的公切線和若干段圓弧spa
爲了方便,把起點終點當作兩個半徑爲$0$的圓也行。code
最煩的就是算兩個圓的公切線了,一共有四條ci
對於靠外面的兩條,咱們把切線、半徑和兩圓心之間的線段連起來,會構成一個直角梯形。get
咱們能夠求出兩圓心連線的傾斜角,進而求出這兩條切線的傾斜角,而後切線的直線方程就能夠寫出來了。it
對於靠裏面的兩條,一樣把切線、半徑和兩圓心之間的線段連起來,會出現兩個類似三角形,一樣能夠把傾斜角求出來。class
接着,判斷這個切線段有沒有被其它的圓擋住。sort
個人比較蒟蒻的作法:先用點到直線距離公式判是否與直線相交,若是相交,就要看交點是否落在線段上。把兩個切點與切線的垂線的截距搞出來,判斷當前圓的圓心的垂線的截距是否介於二者之間。di
YL巨佬說能夠經過向量來搞,算出切點到圓心這個向量在切線上的投影長度,若是小於切線段長度就說明相交了。vi
接着,對同一個圓上的全部切點順次連邊權爲弧長的邊。
最後跑一遍最短路。
細節不少,寫法不盡相同,致使代碼太醜了。
#include<bits/stdc++.h> #define LL long long #define DB double #define RG register #define R RG int using namespace std; const DB EPS=1e-9,PI=acos(-1); const int N=503,M=8*N*N; int n,p,he[M],ne[2*M],to[2*M]; DB x[N],y[N],r[N],w[2*M],dis[M];bool vis[M]; struct Dat{DB w;int x;inline bool operator<(Dat a)const{return w<a.w;}}; struct Nod{DB w;int x;inline bool operator<(Nod a)const{return w>a.w;}}q[M]; vector<Dat>v[N]; inline bool Eq(DB x,DB y){ return fabs(x-y)<EPS; } inline DB sqr(DB x){ return x*x; } inline DB Dis(R i,R j){ return sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j])); } inline DB Ang(DB a){ while(a>PI)a-=2*PI;while(a<-PI)a+=2*PI; return a; } inline bool Cross(DB a,DB b,DB c,DB le,DB ri){ if(le>ri)swap(le,ri); DB t=sqr(a)+sqr(b); for(R i=1;i<=n;++i) if(sqr(r[i])*t-sqr(a*x[i]+b*y[i]+c)>EPS){ DB tmp=b*x[i]-a*y[i]; if(le<=tmp&&tmp<=ri)return 1; } return 0; } inline void Add(R x,R y,DB z){ ne[++p]=he[x];to[he[x]=p]=y;w[p]=z; ne[++p]=he[y];to[he[y]=p]=x;w[p]=z; } int main(){ DB sx,sy,tx,ty,a,b,c,A,B,C,tmp; int id1,id2,p; cin>>sx>>sy>>tx>>ty>>n; for(R i=1;i<=n;++i)cin>>x[i]>>y[i]>>r[i]; n+=2;x[n-1]=sx;y[n-1]=sy;x[n]=tx;y[n]=ty; for(R i=1;i<=n;++i) for(R j=1;j<i;++j){ bool fl=0; if(r[i]>r[j])swap(i,j),fl=1; A=atan2(y[j]-y[i],x[j]-x[i]); B=asin((r[j]-r[i])/Dis(i,j)); for(R tp=0;tp<=1;++tp){ C=Ang(A+B*(tp?-1:1)); if(Eq(fabs(C),PI/2))a=1,b=0; else a=tan(C),b=-1; tmp=Ang(C+PI/2*(tp?-1:1)); c=-a*(x[i]+r[i]*cos(tmp))-b*(y[i]+r[i]*sin(tmp)); if(!Cross(a,b,c,b*x[i]-a*y[i],b*x[j]-a*y[j])){ id1=4*(n*(i-1)+j-1)+tp,id2=4*(n*(j-1)+i-1)+tp; v[i].push_back((Dat){tmp,id1}); v[j].push_back((Dat){tmp,id2}); Add(id1,id2,sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j])-sqr(r[j]-r[i]))); } } B=asin((r[j]+r[i])/Dis(i,j)); for(R tp=0;tp<=1;++tp){ C=Ang(A+B*(tp?-1:1)); if(Eq(fabs(C),PI/2))a=1,b=0; else a=tan(C),b=-1; tmp=sqrt(sqr(r[i])*(sqr(a)+sqr(b))); c=tmp-a*x[i]-b*y[i]; if(!Eq(sqr(r[j])*(sqr(a)+sqr(b)),sqr(a*x[j]+b*y[j]+c))) c=-tmp-a*x[i]-b*y[i]; if(!Cross(a,b,c,b*x[i]-a*y[i],b*x[j]-a*y[j])){ id1=4*(n*(i-1)+j-1)+tp+2,id2=4*(n*(j-1)+i-1)+tp+2;tmp=Ang(C+PI/2*(tp?-1:1)); v[i].push_back((Dat){Ang(tmp+PI),id1}); v[j].push_back((Dat){tmp,id2}); Add(id1,id2,sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j])-sqr(r[j]+r[i]))); } } if(fl)swap(i,j); } for(R i=1;i<=n;++i){ if(!v[i].size())continue; sort(v[i].begin(),v[i].end()); Add(v[i][v[i].size()-1].x,v[i][0].x,(v[i][0].w-v[i][v[i].size()-1].w+2*PI)*r[i]); for(R unsigned j=1;j<v[i].size();++j) Add(v[i][j-1].x,v[i][j].x,(v[i][j].w-v[i][j-1].w)*r[i]); } memset(dis,127,sizeof(dis)); q[p=1]=(Nod){dis[v[n-1][0].x]=0,v[n-1][0].x}; while(p){ R x=q[1].x;pop_heap(q+1,q+p--+1); if(vis[x])continue;vis[x]=1; for(R y,i=he[x];i;i=ne[i]) if(dis[y=to[i]]>dis[x]+w[i]) q[++p]=(Nod){dis[y]=dis[x]+w[i],y},push_heap(q+1,q+p+1); } printf("%.1lf\n",dis[v[n][0].x]); return 0; }