intさわだんのBlack History

刹那的レジェンドになりたい。

ICPC 国内予選2012E 鎖中経路 AOJ1183

問題文
judge.u-aizu.ac.jp


パッと見DP感があるな~と思って少し考えたらほんとにDPだった。
解説:
i番目の円とi+1番目の円の交点は2つある。その二つの交点に適当に0個目、1個目の点とすると。
dp[i][j] =0番目の円(一番端の円)から、 i番目の円とi+1番目のj(=0,1)個目の交点にたどり着くまでの最小の長さ
と定義できて、それを典型的なDPで回せば求まる。
if(0~iの0,1個目の交点から今の点までの線分が、任意の円に重なっている場合) dp[i][j] = min( dp[0~i][0,1] + (0~iの0,1個目の交点から今の点までの距離) )
で求められる。
if文の判定は線分の交差判定をぶん回せばよい

dist関数の引数をint型にするという珍プレーをして無限に時間を消費したので気を付けていきたい(まあライブラリを使えばこういうミスはないと思うが、)

#include <bits/stdc++.h>

using namespace std;


#define EPS (1e-10)
#define equals(a, b) (fabs((a) - (b)) < EPS)
 
struct Point{
    double x, y;
     
    Point(double x = 0, double y = 0): x(x), y(y) {}
 
    Point operator + (Point p) { return Point(x + p.x, y + p.y); }
    Point operator - (Point p) { return Point(x - p.x, y - p.y); }
    Point operator * (double a) { return Point(a * x, a * y); }
    Point operator / (double a) { return Point(x / a, y / a); }
 
    double norm() {return x * x + y * y; }
    double abs() {return sqrt(norm()); }
 
    bool operator < (const Point &p) const{
        return (x != p.x ? x < p.x : y < p.y);
    }
 
    bool operator == (const Point &p) const{
        return (fabs(x - p.x) < EPS && fabs(y - p.y) < EPS);
    }
};
 
typedef Point Vector;
 
struct Circle{
    Point c;
    double r;
 
    Circle(Point c = Point(), double r = 0.0): c(c), r(r) {}
};
 
double norm(Vector a){
    return a.x * a.x + a.y * a.y;
}
 
double abs(Vector a){
    return sqrt(norm(a));
}
 
double arg(Vector p){
    return atan2(p.y, p.x);
}
 
Vector polar(double a, double r){
    return Point(cos(r) * a, sin(r) * a);
}
 
pair<Point, Point> getCrossPoints(Circle c1, Circle c2){
    double d = abs(c1.c - c2.c);
    double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    double t = arg(c2.c - c1.c);
    return make_pair(c1.c + polar(c1.r, t + a), c1.c + polar(c1.r, t - a));
}

int n;

double dist(double x1,double y1,double x2,double y2){
    return sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)  );
}

bool judgeIenter(double ax,double ay,double bx,double by,double cx,double cy,double dx,double dy) {
    double ta = (cx - dx) * (ay - cy) + (cy - dy) * (cx - ax);
    double tb = (cx - dx) * (by - cy) + (cy - dy) * (cx - bx);
    double tc = (ax - bx) * (cy - ay) + (ay - by) * (ax - cx);
    double td = (ax - bx) * (dy - ay) + (ay - by) * (ax - dx);

   // return (tc * td < 0 && ta * tb < 0); //端点を含まない場合
     return (tc * td <= 0 && ta * tb <= 0); // 端点を含む場合
}
double INF = 10000000;
double dp[110][2];
double d[110][3];
double p[110][2][2];
bool check(int a,int aa,int b,int bb){
    double ax,ay,bx,by;
    if(a == -1){
        ax = d[0][0];
        ay = d[0][1];
    }else{
        ax = p[a][aa][0];
        ay = p[a][aa][1];
    }
    if(b == n-1){
        bx = d[n-1][0];
        by = d[n-1][1];
    }else{
        bx = p[b][bb][0];
        by = p[b][bb][1];
    }
    bool flag = true;
    for(int i = max(0,a);i < b;i++){
        if(judgeIenter(ax,ay,bx,by,p[i][0][0],p[i][0][1],p[i][1][0],p[i][1][1]) == false){
            flag = false;
            break;
        } 
    }
    
    return (flag);
}



int main(){
    while(true){
        cin >> n;
        if(n == 0)break;
        for(int i = 0;i <= n+1;i++){
            dp[i][0] = INF;
            dp[i][1] = INF;
        }
        for(int i = 0;i < n;i++){
            for(int j = 0;j < 3;j++){
                cin >> d[i][j];
            }
        }
        for(int i = 0;i < n-1;i++){
            Circle c1 = {Point(d[i][0], d[i][1]), d[i][2]};
            Circle c2 = {Point(d[i+1][0], d[i+1][1]), d[i+1][2]};
            pair<Point, Point> ansp = getCrossPoints(c1, c2);
            p[i][0][0] = ansp.first.x;
            p[i][0][1] = ansp.first.y;
            p[i][1][0] = ansp.second.x;
            p[i][1][1] = ansp.second.y;
        }
        for(int i = 0;i < 2;i++){
            dp[0][i] = dist(d[0][0],d[0][1],p[0][i][0],p[0][i][1]);
        }
        
        for(int i = 1;i < n-1;i++){
            for(int k = 0;k < 2;k++){
                double nx = p[i][k][0],ny = p[i][k][1];
                if(check(-1,0,i,k)) dp[i][k] = dist(d[0][0],d[0][1],nx,ny);
                for(int j = 0;j < i;j++){
                    for(int kk = 0;kk < 2;kk++){
                        if(check(j,kk,i,k))dp[i][k] = min(dp[i][k],dp[j][kk]+dist(p[j][kk][0],p[j][kk][1],nx,ny));
                    }
                }
            }
        }
        double ans = INF;
        for(int i = -1;i < n-1;i++){
            for(int k = 0;k < 2;k++){
                if(check(i,k,n-1,0)){
                    if(i == -1)ans = min(ans,dist(d[0][0],d[0][1],d[n-1][0],d[n-1][1]));
                    else ans = min(ans,dp[i][k]+dist(p[i][k][0],p[i][k][1],d[n-1][0],d[n-1][1]));
                }
            }
        }
        printf("%lf\n",ans);
    }
}