易知最短路一定是以圆心或者两圆交点作为中间点到达的。所以把这些点拿出来建图跑最短路就够了。
现在的问题就是,给定两个点,能否连边 add(a,b,dist(a,b))
题目要求,ab线段必须完全在圆上,所以可以求出ab线段和所有圆的所有交点,对于任意相邻两个交点,它们必处于同一个圆内,否则不可达。点的编号用map就够了(一开始我以为double有精度问题无法map,用两个longlong保存然后乘上1000000000,后来发现没问题,应该是这题都是整点,精度要求不高的原因吧)
#include <cstdio> #include <iostream> #include <algorithm> #include <queue> #include <map> #include <cmath> #include <cstring> using namespace std; #define pi acos(-1.0) #define eps 1e-10 int cmp(double x) { if(fabs(x)<eps) return 0; if(x>0) return 1; return -1; } double sqr(double x) { return x*x; } struct point { double x,y; point(){}; point(double a,double b):x(a),y(b){}; void out() { printf("%lf %lf\n",x,y); } void input() { scanf("%lf%lf",&x,&y); } friend point operator +(const point &a,const point &b) { return point(a.x+b.x,a.y+b.y); } friend point operator -(const point &a,const point &b) { return point(a.x-b.x,a.y-b.y); } friend bool operator ==(const point &a,const point &b) { return cmp(a.x-b.x)==0&&cmp(a.y-b.y)==0; } bool operator <(const point &a) const { if(x==a.x) return y<a.y; return x<a.x; } friend point operator *(const point &a,const double &b) { return point(a.x*b,a.y*b); } friend point operator *(const double &a,const point &b) { return point(a*b.x,a*b.y); } friend point operator /(const point &a,const double &b) { return point(a.x/b,a.y/b); } double norm() { return sqrt(sqr(x)+sqr(y)); } }; double det(const point &a,const point &b) { return a.x*b.y-a.y*b.x; } double dot(const point&a,const point &b) { return a.x*b.x+a.y*b.y; } double dist(const point &a,const point &b) { return (a-b).norm(); } point rotate_point(const point &p,double A) { double tx=p.x,ty=p.y; return point(tx*cos(A)-ty*sin(A),tx*sin(A)+ty*cos(A)); } point rotate(const point &p,double cost,double sint) { double x=p.x,y=p.y; return point(x*cost-y*sint,x*sint+y*cost); } pair<point,point> crosspoint(point ap,double ar,point bp,double br) { double d=(ap-bp).norm(); double cost = (ar*ar + d*d -br*br)/(2*ar*d); double sint=sqrt(1.0-cost*cost); point v=(bp-ap)/(bp-ap).norm()*ar; return make_pair(ap+rotate(v,cost,-sint),ap+rotate(v,cost,sint)); } void circle_cross_line(point a,point b,point o,double r,point ret[],int &num) { double x0 = o.x ,y0 = o.y; double x1 = a.x, y1 = a.y; double x2 = b.x, y2 = b.y; double dx = x2-x1, dy = y2-y1; double A = dx*dx+dy*dy; double B = 2*dx*(x1-x0) + 2*dy*(y1-y0); double C = sqr(x1-x0) + sqr(y1-y0)-sqr(r); double delta = B*B-4*A*C; if( cmp(delta) >= 0) { double t1 = (-B - sqrt(delta)) / (2*A); double t2 = (-B + sqrt(delta)) / (2*A); if(cmp(t1-1)<=0 && cmp(t1)>=0) ret[num++] = point(x1+t1*dx,y1+t1*dy); if(cmp(t2-1) <=0 && cmp(t2)>=0) ret[num++] = point(x1+t2*dx,y1+t2*dy); } } struct rad { point c; double r; }ra[300]; int n; map<point,int> mp; int ID; int que[123456]; point xx[100005]; struct node2 { int v,next; double w; }e[123456]; int head[12345]; bool in[12345]; double d[12345]; int en; void add(int a,int b,double c) { // printf("%d %d %lf\n",a,b,c); e[en].v=b; e[en].w=c; e[en].next=head[a]; head[a]=en++; e[en].v=a; e[en].w=c; e[en].next=head[b]; head[b]=en++; } void spfa(int st) { queue<int> q; memset(in,0,sizeof(in)); for(int i=1;i<=ID;i++) d[i]=1000000000; q.push(st); in[st]=1; d[st]=0; int tmp; while(!q.empty()) { tmp=q.front();q.pop(); in[tmp]=0; for(int i=head[tmp];~i;i=e[i].next) { if(d[e[i].v]>d[tmp]+e[i].w) { d[e[i].v]=d[tmp]+e[i].w; if(!in[e[i].v]) { in[e[i].v]=1; q.push(e[i].v); } } } } } bool inra(point &x,point &y) { for(int i=1;i<=n;i++) { if(dist(x,ra[i].c)<=ra[i].r+eps && dist(y,ra[i].c)<=ra[i].r+eps) { return true; } } return false; } point my[12345]; bool cal(point &a,point &b) { my[0]=a; int top=1; for(int i=1;i<=n;i++) { circle_cross_line(a,b,ra[i].c,ra[i].r,my,top); } my[top++]=b; sort(my,my+top); for(int i=1;i<top;i++) { if(!inra(my[i-1],my[i])) return false; } return true; } int main() { int ca=1; int cas; scanf("%d",&cas); while(cas--) { mp.clear(); scanf("%d",&n); ID=0; for(int i=1;i<=n;i++) { ra[i].c.input(); scanf("%lf",&ra[i].r); } printf("Case %d: ",ca++); if(cal(ra[1].c,ra[n].c)) //冲榜都靠这个特判#_# { printf("%.4f\n",dist(ra[1].c,ra[n].c)); continue; } en=0; memset(head,-1,sizeof(head)); int top=0; for(int i=1;i<=n;i++) { mp[ra[i].c]=++ID; que[top++]=ID; xx[ID]=ra[i].c; for(int j=i+1;j<=n;j++) { if(dist(ra[i].c,ra[j].c)>(ra[i].r+ra[j].r)+0.0) continue; pair<point,point> tmp=crosspoint(ra[i].c,ra[i].r,ra[j].c,ra[j].r); if(mp[tmp.first]==0) { mp[tmp.first]=++ID; que[top++]=ID; xx[ID]=tmp.first; } if(mp[tmp.second]==0) { mp[tmp.second]=++ID; que[top++]=ID; xx[ID]=tmp.second; } } } mp[ra[n].c]=++ID; que[top++]=ID; xx[ID]=ra[n].c; for(int i=0;i<top;i++) { for(int j=i+1;j<top;j++) { if(cal(xx[que[i]],xx[que[j]])) { add(que[i],que[j],dist(xx[que[i]],xx[que[j]])); } } } spfa(1); if(d[ID]>=1000000000) puts("No such path."); else printf("%.4f\n",d[ID]); } return 0; } /* 99 2 0 0 1 2 0 1 2 0 0 1 4 1 2 4 -4 0 1 -1 0 2 1 0 2 4 0 1 3 -3 0 1 0 0 2 0 3 1 3 -3 0 1 0 0 2 -2 -1 1 3 -3 0 1 -2 -1 1 0 0 2 4 2 2 2 2 -2 2 -2 2 2 -2 -2 2 2 0 0 3 1 0 1 3 0 0 1 2 2 2 3 0 1 3 0 0 1 3 0 1 2 2 2 ans: 2.0000 No 8.0000 4.8284 1.4142 3.0000 6.8284 1.0000 3.0654 2.8284 */