AOJ 1183 - Chain-Confined Path
問題概要
平面上のn個の円環から構成される鎖がある。最初と最後の円はそれぞれ、次の円、1つ前の円のみと交差し、他の各円は隣の2つの円のみと交差する。以下の2つの条件を満たす最短経路を求めよ。
- 経路は、最初の円の中心がスタートで、最後の円の中心がゴール。
- 経路は鎖中にある。すなわち、経路上のすべての点は、少なくとも1つの円の内側もしくは円周上にある。
制約
- 3 ≤ n ≤ 100
- 0 ≤ xi ≤ 1000
- 0 ≤ yi ≤ 1000
- 1 ≤ ri ≤ 25
- 隣り合う2つの円は必ず2点で交差する。
解法
線分同士の交差判定 + 2円の交点 + ダイクストラ。
まず、隣り合う2円の交点を全て求める。Point型のvectorに最初の円の中心点、それらの点、最後の円の中心点の順に格納する。
次に、そのvectorでi < j となる全ての2点について距離を求める。このとき、その2点の間に円が存在する場合は、円の2つの交点を線分とし、また、その2点も線分とし、それらの交差判定をする。これが交差するならば、有効なエッジとして追加する。交差しなければ問題の条件を満たさないためエッジを張らない。
そして、最初の円の中心点、全ての隣接する円の交点、最後の円の中心点をノードとしてダイクストラする。
コード
#include <bits/stdc++.h> using namespace std; #define MAX_N 110 #define INF 1e9 #define EPS 1e-10 #define equal(a,b) (fabs(a-b) < EPS) #define not_equal(a,b) (!equal(a,b)) #define lt(a,b) (a-b < -EPS) struct Point{ double x,y; Point(){} Point(double x,double y) : x(x),y(y) {} Point operator + (const Point &p)const{ return Point(x+p.x,y+p.y); } Point operator - (const Point &p)const{ return Point(x-p.x,y-p.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); } bool operator < (const Point &p)const{ return x != p.x ? x < p.x : y < p.y; } }; Point operator * (const Point &a,const Point &b){ return Point(a.x*b.x-a.y*b.y,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 cross(const Point &a,const Point &b){ return a.x*b.y - b.x*a.y; } double norm(const Point &p){ return dot(p,p); } double abs(const Point &p){ return sqrt(norm(p)); } double getDistance(const Point &a,const Point &b){ return sqrt(pow(a.x-b.x,2) + pow(a.y-b.y,2)); } istream &operator >> (istream &is,Point &p){ return is >> p.x >> p.y; } typedef Point Vector; #define COUNTER_CLOCKWISE 1 #define CLOCKWISE -1 #define ONLINE_BACK 2 #define ONLINE_FRONT -2 #define ON_SEGMENT 0 typedef Point Vector; int ccw(Point p0,Point p1,Point p2){ Vector a = p1 - p0; Vector b = p2 - p0; if(cross(a,b) > EPS){ return COUNTER_CLOCKWISE; } if(cross(a,b) < -EPS){ return CLOCKWISE; } if(dot(a,b) < -EPS){ return ONLINE_BACK; } if(norm(a) < norm(b)){ return ONLINE_FRONT; } return ON_SEGMENT; } struct Segment{ Point s,t; Segment(){} Segment(Point s,Point t) : s(s),t(t) {} }; struct Circle{ Point p; double r; Circle(){} Circle(Point p,double r) : p(p),r(r) {} }; bool isIntersectSS(const Segment &a,const Segment &b){ Point s[2] = {a.s,a.t}, t[2] = {b.s,b.t}; return ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1]) <= 0 && ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1]) <= 0; } vector<Point> crosspointCC(const Circle &a,const Circle &b){ vector<Point> res; double d = abs(a.p-b.p); double rc = (a.r*a.r-b.r*b.r+d*d)/(2.0*d); double rs = sqrt(a.r*a.r-rc*rc); Point p = (b.p-a.p)/d; res.push_back(Point(a.p+p*Point(rc,-rs))); res.push_back(Point(a.p+p*Point(rc,rs))); return res; } struct P{ double d; int n,p; P(double d,int n,int p) : d(d),n(n),p(p) {} bool operator < (const P &p)const{ if(d != p.d){ return d > p.d; } } }; double dist[MAX_N][2][MAX_N][2]; double solve(int N){ double d[MAX_N][2]; fill(d[0],d[0]+MAX_N*2,INF); d[0][0] = 0.0; priority_queue<P> Q; Q.push(P(0,0,0)); while(!Q.empty()){ P p = Q.top(); Q.pop(); int idx = p.n,pos = p.p; if(lt(d[idx][pos],p.d)){ continue; } if(idx == N){ return p.d; } for(int i = idx+1 ; i <= N ; i++){ for(int j = 0 ; j < 2 ; j++){ if(dist[idx][pos][i][j] == INF){ continue; } if(p.d+dist[idx][pos][i][j] < d[i][j]){ d[i][j] = p.d+dist[idx][pos][i][j]; Q.push(P(d[i][j],i,j)); } } } } return -1; } void init(int N){ for(int i = 0 ; i < N ; i++){ for(int j = 0 ; j < 2 ; j++){ for(int k = 0 ; k <= N ; k++){ for(int l = 0 ; l < 2 ; l++){ dist[i][j][k][l] = INF; } } } } } int main(){ int N; while(cin >> N,N){ vector<Point> G[MAX_N]; vector<Circle> cirs(N); init(N); for(int i = 0 ; i < N ; i++){ cin >> cirs[i].p >> cirs[i].r; if(i == 0){ G[i].push_back(cirs[0].p); G[i].push_back(cirs[0].p); }else{ vector<Point> cp = crosspointCC(cirs[i-1],cirs[i]); G[i].push_back(cp[0]); G[i].push_back(cp[1]); } } G[N].push_back(cirs[N-1].p); G[N].push_back(cirs[N-1].p); for(int i = 0 ; i < N ; i++){ for(int j = i+1 ; j <= N ; j++){ Point p1[2] = {G[i][0],G[i][1]}; Point p2[2] = {G[j][0],G[j][1]}; for(int k = 0 ; k < 2 ; k++){ for(int l = 0 ; l < 2 ; l++){ Segment a = Segment(p1[k],p2[l]); bool ok = true; for(int m = i ; m < j ; m++){ Segment b = Segment(G[m][0],G[m][1]); if(!isIntersectSS(a,b)){ ok = false; break; } } if(ok){ dist[i][k][j][l] = getDistance(p1[k],p2[l]); } } } } } printf("%.8f\n",solve(N)); } return 0; }