题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4063
题目大意:
有n个圆,给出圆心和半径,第一个和最后一个圆的圆心分别为起点和终点,求起点是否可达终点,且路径上所有点必须在给出的圆内或圆上
分析:
画图可以发现,从起点出发的最短路径,必定经过圆心,或者两圆的交点,所以找出所有的两圆并去重,然后任意两点之间检查是否可达。
可达性的检查为,作出两点间的线段,同时检查该线段所在直线与所有圆的交点,保存在线段上的交点,最后对所有交点排序,若相邻两交点间的线段被某个圆包括则合法,否则非法,该两点不可达,不可建边。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZyRpKCI-1622049927811)(http://hi.csdn.net/attachment/201110/7/6627258_1317988620BYI7.jpg)]
两点之间的边的权值即是两点距离,然后对于建出的图直接跑dij或spfa即可,由于一开始排序去重了,点的顺序被打乱,所以需要一次遍历找回起点和终点,即与第一个和最后一个圆的圆心相同的点。
代码:
注:直接套了kuangbin的计算几何板子,很长
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
const int maxp = 1010;
int sgn(double x){
if (fabs(x)<eps) return 0;
if (x<0) return -1;
else return 1;
}
inline double sqr(double x)
{
return x*x;
}
struct Point{
double x,y;
Point(){}
Point(double _x,double _y){
x = _x;
y = _y;
}
void input(){
scanf("%lf%lf",&x,&y);
}
void output(){
printf("%2.f,%.2f\n",x,y);
}
bool operator == (Point b)const{
return sgn(x-b.x)==0&& sgn(y-b.y)==0;
}
bool operator < (Point b)const{
return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x;
}
Point operator - (const Point & b)const{
return Point(x-b.x,y-b.y);
}
//叉积
double operator ^ (const Point &b)const{
return x*b.y-y*b.x;
}
//点积
double operator * (const Point &b)const{
return x*b.x + y*b.y;
}
double len()
{
return hypot(x,y);
}
double len2()
{
return x*x+y*y;
}
double distance(Point p)
{
return hypot(x-p.x,y-p.y);
}
Point operator + (const Point & b) const
{
return Point(x+b.x,y+b.y);
}
Point operator *(const double& k)const{
return Point(x*k,y*k);
}
Point operator / (const double &k) const{
return Point(x/k,y/k);
}
double rad(Point a,Point b)
{
Point p = *this;
return fabs(atan2 (fabs((a-p)^(b-p)),(a-p)*(b-p)));
}
Point trunc(double r)
{
double l = len();
if (!sgn(l)) return *this;
r /=l ;
return Point (x*r,y*r);
}
Point rotleft(){
return Point(-y,x);
}
Point rotright(){
return Point(y,-x);
}
Point rotate(Point p,double angle)
{
Point v = (*this) - p;
double c = cos(angle) , s = sin(angle);
return Point(p.x+v.x*c - v.y*s,p.y+v.x*s + v.y*c);
}
};
struct Line{
Point s,e;
Line(){}
Line(Point _s,Point _e)
{
s = _s;
e = _e;
}
bool operator == (Line v)
{
return (s==v.s)&&(e==v.e);
}
Line (Point p,double angle)
{
s = p;
if (sgn(angle - pi/2) == 0)
{
e = (s + Point(0,1));
}
else
e = (s +Point(1,tan(angle)));
}
Line (double a,double b,double c)
{
if (sgn(a)==0)
{
s = Point(0,-c/b);
e = Point(1,-c/b);
}
else if (sgn(b)==0)
{
s = Point(-c/a,0);
e = Point(-c/a,1);
}
else
{
s = Point(0,-c/b);
s = Point(1,(-c-a)/b);
}
}
void input()
{
s.input();
e.input();
}
void adjust()
{
if (e<s) swap(s,e);
}
double length()
{
return s.distance(e);
}
double angle(){
double k = atan2(e.y-s.y,e.x-s.x);
if (sgn(k)<0) k+= pi;
if (sgn(k-pi)==0) k -= pi;
return k;
}
int relation(Point p)
{
int c = sgn((p-s)^(e-s));
if (c<0) return 1;
else if (c>0) return 2;
else return 3;
}
bool pointonseg(Point p)
{
return sgn((p-s)^(e-s)) ==0 &&sgn((p-s)*(p-e))<=0;
}
bool parallel(Line v)
{
return sgn((e-s)^(v.e-v.s)) ==0;
}
int segcrossseg(Line v)
{
int d1 = sgn((e-s)^(v.s-s));
int d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s));
int d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) ==-2 && (d3^d4)==-2 ) return 2;
return (d1==0&& sgn((v.s-s)*(v.s-e))<=0)||
(d2 ==0 && sgn((v.e-s)*(v.e-e))<=0)||
(d3 ==0 && sgn((s-v.s)*(s-v.e))<=0)||
(d4 ==0 && sgn((e-v.s)*(e-v.e))<=0);
}
Point crosspoint(Line v)
{
double a1 = (v.e-v.s)^(s-v.s);
double a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p)
{
return fabs((p-s)^(e-s))/length();
}
double dispointtoseg(Point p)
{
if (sgn((p-s)*(e-s))<0|| sgn((p-e)*(s-e))<0)
return min(p.distance(s),p.distance(e));
return dispointtoline(p);
}
Point lineprog(Point p)
{
return s + ( ((e-s)*((e-s)*(p-s)))/((e-s).len2()));
}
};
struct circle{
Point p;
double r;
circle() {}
circle(Point _p , double _r)
{
p = _p;
r = _r;
}
circle(Point a, Point b, Point c)
{
Line u = Line((a+b)/2,((a+b)/2)+((b-a).rotleft()));
Line v = Line((b+c)/2,((b+c)/2)+((c-b).rotleft()));
p = u.crosspoint(v);
r = p.distance(a);
}
void input()
{
p.input();
scanf("%lf",&r);
}
void output()
{
printf("%.2f,%.2f,%.2f\n",p.x,p.y,r);
}
bool operator == (circle v)
{
return (p==v.p)&&sgn(r-v.r)==0;
}
bool operator < (circle v)const
{
return ((p<v.p)||((p==v.p)&&sgn(r-v.r)<0));
}
double area()
{
return pi*r*r;
}
double circumference()
{
return 2*pi*r;
}
int relation(Point b)
{
double dst = b.distance(p);
if (sgn(dst-r)<0) return 2;
else if (sgn(dst-r)==0) return 1;
return 0;
}
int relationseg(Line v)
{
double dst = v.dispointtoseg(p);
if (sgn(dst-r) < 0) return 2;
else if (sgn(dst -r) ==0) return 1;
return 0;
}
int relationline (Line v)
{
double dst = v.dispointtoline(p);
if (sgn(dst-r)<0) return 2;
else if (sgn(dst-r)==0) return 1;
return 0;
}
int relationcircle(circle v)
{
double d = p.distance(v.p);
if (sgn(d-r-v.r)>0) return 5;
if (sgn(d-r-v.r)==0) return 4;
double l = fabs(r-v.r);
if (sgn(d-r-v.r)<0 && sgn(d-l)>0) return 3;
if (sgn(d-l) ==0 ) return 2;
if (sgn(d-l)< 0) return 1;
}
int pointcrosscircle(circle v,Point& p1,Point& p2)
{
int rel = relationcircle(v);
if (rel==1 || rel==5) return 0;
double d = p.distance(v.p);
double l = (d*d+r*r-v.r*v.r)/(2*d);
double h = sqrt(r*r-l*l);
Point tmp = p+ (v.p-p).trunc(l);
p1 = tmp + ((v.p-p).rotleft().trunc(h));
p2 = tmp + ((v.p-p).rotright().trunc(h));
if (rel==2 || rel ==4)
return 1;
return 2;
}
int pointcorssline(Line v, Point &p1 , Point &p2)
{
if (!(*this).relationline(v)) return 0;
Point a = v.lineprog(p);
double d = v.dispointtoline(p);
d = sqrt(r*r-d*d);
if (sgn(d)==0)
{
p1 = a;
p2 = a;
return 1;
}
p1 = a + (v.e-v.s).trunc(d);
p2 = a - (v.e-v.s).trunc(d);
return 2;
}
}c[50];
const int MAXN = 1000;
const int MAXM = 500000;
Point arr[5000];
int n,pc;
struct Edge{
int from, to, next;
double val;
int id;
}edge[MAXM];
struct Node{
int x;
double d;
Node(){}
Node( int a, double b ): x( a ), d( b ){}
bool operator < ( Node a ) const{
return d > a.d;
}
};
int head[MAXN],cnt;
double dis[MAXN];
void init(){
cnt = 0;
for (int i = 0 ; i < MAXN ; i ++)
dis[i] = inf;
memset( head, -1, sizeof( head ) );
}
void add( int from, int to, double val ){
edge[cnt].from = from;
edge[cnt].to = to;
edge[cnt].val = val;
edge[cnt].next = head[from];
head[from] = cnt++;
}
void dij( int s ){
dis[s] = 0;
priority_queue<Node> Q;
Q.push( Node( s, dis[s] ) );
while( !Q.empty() ){
Node temp = Q.top(); Q.pop();
int x = temp.x;
if( temp.d > dis[x] ) continue;
for( int k = head[x]; ~k; k = edge[k].next ){
int y = edge[k].to;
if( dis[y] > dis[x] + edge[k].val ){
dis[y] = dis[x] + edge[k].val;
Q.push( Node( y, dis[y] ) );
}
}
}
}
bool check(Point a,Point b)
{
if (b<a)
swap(a,b);
Point cs[500],p,q;
int pt = 0;
Line u = Line(a,b);
for (int i = 1 ; i <= n ; i ++)
{
int res = c[i].pointcorssline(u,p,q);
if (res==1&&u.pointonseg(p))
cs[++pt] = p;
else if (res==2)
{
if (u.pointonseg(p))
cs[++pt] = p;
if (u.pointonseg(q))
cs[++pt] = q;
}
}
sort(cs+1,cs+pt+1);
pt = unique(cs+1,cs+pt+1) -cs - 1;
for (int i = 2 ; i <= pt ; i ++)
{
// if ((cs[i]<a)||(b<cs[i]))
// continue;
int flag = 0;
Point mid = Point((cs[i-1].x+cs[i].x)/2,(cs[i-1].y+cs[i].y)/2);
for (int j = 1 ; j <= n ; j ++)
{
if (sgn(mid.distance(c[j].p)-c[j].r)<=0)
{
// cs[i-1].output();
// cs[i].output();
// c[j].output();
flag = 1;
break;
}
}
if (!flag)
return false;
}
return true;
}
int main()
{
int T,t=1;
Point p,q;
scanf("%d",&T);
while (T--)
{
init();
scanf("%d",&n);
for (int i = 1; i <= n ; i ++)
{
c[i].input();
arr[i] = c[i].p;
}
pc = n;
for (int i = 1 ; i <= n ; i ++)
{
for (int j = i + 1 ; j <= n ; j ++)
{
int res = c[i].pointcrosscircle(c[j],p,q);
if (res==1)
arr[++pc] = p;
else if (res==2)
{
arr[++pc] = p;
arr[++pc] = q;
}
}
}
sort(arr+1,arr+pc+1);
// printf("pre pc = %d\n",pc);
pc = unique(arr+1,arr+pc+1)- arr - 1;
// printf("after pc = %d\n",pc);
int st,ed;
for (int i = 1 ; i <= pc ; i ++)
{
if (arr[i]==c[1].p) st = i;
if (arr[i]==c[n].p) ed = i;
}
// printf("st= %d\ted= %d\n",st,ed);
for (int i = 1 ; i <= pc ; i ++)
{
for (int j = i + 1; j <= pc ; j ++)
{
if (check(arr[i],arr[j]))
{
//printf("-------------\n");
//arr[i].output();
//arr[j].output();
//printf("dis = %f\n",arr[i].distance(arr[j]));
//printf("-------------\n");
add(i,j,arr[i].distance(arr[j]));
add(j,i,arr[i].distance(arr[j]));
}
}
}
printf("Case %d: ",t++);
dij(st);
if (sgn(dis[ed]-inf)==0)
printf("No such path.\n");
else
printf("%.4f\n",dis[ed]);
}
return 0;
}