Problem:
Problem L: RedBlue

Story

天空都市AIZUのUZIA高校では、競技プログラミングの部活動がとても盛んである。
この部活には、n人のRed Coderとn人のBlue Coderが所属している。

ある日の部活の時間に、Red CoderとBlue Coderでペアを組み、この部活からn組、KCPというコンテストに参加することとなった。この高校では、ペアを組む学生同士は握手するという習わしがあるので、部員たちは今すぐ自分のパートナーを見つけることにした。

部員たちは全速力で走るため、まっすぐにしか進めない。また、部員たちは、それぞれの部員が移動する距離の総和をできるだけ小さくしたいと思っている。

なお、部室には2つの円形のテーブルが置いてある。

Problem

二次元平面上に2つの円、n個の赤色の点、n個の青色の点がある。
2つの円の中心座標はそれぞれ(x1, y1), (x2, y2)であり、半径はそれぞれr1, r2である。
赤い点iは、座標(rxi, ryi)にあり、青い点jは、座標(bxj, byj)にある。

あなたは、以下の操作をn回繰り返す必要がある。

まだ選ばれていない点の中から、赤色の点と青色の点を１つずつ選んで、2点の共通の目的地を設定し、その目的地に向かって2点をそれぞれまっすぐ移動させる。
目的地は二次元平面上であれば、どこに設定しても構わない。ただし、選んだ2点は移動の際に円の内部を通過することはできないので、そのような移動が発生する目的地を設定することはできない。

n回の操作をしたときの移動距離の総和を最小化せよ。
n回の操作を行えない場合は、代わりに"Impossible" (""は除く) と出力せよ。

Input

入力は以下の形式で与えられる。

n
x1 y1 r1
x2 y2 r2
rx1 ry1
rx2 ry2
...
rxn ryn
bx1 by1
bx2 by2
...
bxn byn

入力は全て整数で与えられる。

1行目にnが与えられる。

2行目にx1, y1, r1が空白区切りで与えられる。

3行目にx2, y2, r2が空白区切りで与えられる。

4から3+n行に赤い点の座標(rxi, ryi)が空白区切りで与えられる。

4+nから3+2×n行に青い点の座標(bxj, byj)が空白区切りで与えられる。

Constraints

入力は以下の条件を満たす。

1 ≤ n ≤ 100

-1000 ≤ xi, yi ≤ 1000

1 ≤ ri ≤ 50

-1000 ≤ rxi, ryi, bxi, byi ≤ 1000

同じ座標に複数の点が存在していることはない

任意の円の半径を絶対値10-9以内で変化させても、高々絶対値10-3しか変化しない

任意の円の半径を絶対値10-9以内で変化させても、"Impossible"なケースは"Impossible"のままである

解は10000を超えない

どの点も、円から10-3以上離れていて、点が円周上に存在したり、点が円に内包されることはない

2つの円は共通面積を持たず、10-3以上離れていることが保証される

Output

n回の操作をしたときの移動距離の総和の最小値を1行に出力せよ。なお、出力はジャッジ解の出力との絶対誤差が10-2以内であれば許容される。

n回の操作を行えない場合は、代わりに"Impossible" (""は除く) と出力せよ。

Sample Input 1

2
3 3 2
8 3 2
0 3
3 7
8 0
8 7

Sample Output 1

13.8190642862

Sample Input 2

2
3 3 2
8 3 2
3 0
3 7
8 0
8 7

Sample Output 2

10.0000000000

Sample Input 3

2
3 3 2
8 3 2
0 0
0 5
11 0
11 5

Sample Output 3

22.0000000000

Sample Input 4

1
10 10 10
31 10 10
15 19
26 1

Sample Output 4

Impossible

点同士をつなぐことはできません。

Solution in Kotlin (judge verdict: Wrong Answer)

import java.lang.Math.abs
import java.lang.Math.sqrt

data class Point(val x:Double, val y:Double) {
    operator fun plus(other:Point):Point = Point(x + other.x, y + other.y)
    operator fun minus(other:Point):Point = Point(x - other.x, y - other.y)
}
data class Vector(val x:Double, val y:Double) {
    val length:Double
        get() = sqrt(x * x + y * y)
    constructor(from:Point, to:Point):this(to.x - from.x, to.y - from.y)
}
data class Circle(val center:Point, val radius:Double)
data class Edge(val a:Point, val b:Point){
    val length:Double
        get() = (a - b).let{sqrt(it.x * it.x + it.y * it.y)}
}
data class Angle(val sin:Double, val cos:Double) {
    companion object {
        private const val e:Double = 0.00000000001
    }
    operator fun plus(other: Angle): Angle = Angle(sin * other.cos + cos * other.sin, cos * other.cos - sin * other.sin)
    operator fun minus(other: Angle): Angle = Angle(sin * other.cos - cos * other.sin, cos * other.cos + sin * other.sin)
    override operator fun equals(other: Any?): Boolean {
        return when (other) {
            is Angle -> (cos - other.cos).let { it * it < e } && (sin - other.sin).let { it * it < e }
            else -> false
        }
    }
    override fun hashCode(): Int {
        return super.hashCode()
    }
}
class Line private constructor(private val mA:Double, private val mB:Double, private val mC:Double, private val mD:Double){
    val inclination:Angle
        get() = sqrt(mA * mA + mC * mC).let{ len -> (sign(mA) * sign(mC)).let{Angle(abs(mC) / len, it * abs(mA) / len)}}
    constructor(point:Point, angle:Angle):this(angle.cos, point.y, angle.sin, point.x)
    constructor(from:Point, to:Point):this(from.x - to.x, to.y, from.y - to.y, to.x)
    companion object {
        fun crossPoint(a:Line, b:Line):Point? {
            if (isParallel(a, b)) return null
            return Point((b.mA * a.mC * a.mD - a.mA * b.mC * b.mD - a.mA * b.mA * a.mB + a.mA * b.mA * b.mB) / (b.mA * a.mC - a.mA * b.mC),
                    (a.mA * a.mB * b.mC - b.mA * b.mB * a.mC - a.mC * b.mC * a.mD + a.mC * b.mC * b.mD) / (a.mA * b.mC - b.mA * a.mC))
        }
        private fun isParallel(a:Line, b:Line):Boolean{
            return a.inclination == b.inclination
        }
    }
}
fun sign(value:Double):Double {
    return if (value >= 0) 1.0 else -1.0
}
fun distance(a:Point, b:Point):Double{
    return (a - b).let{sqrt(it.x * it.x + it.y * it.y)}
}
fun Point.toVectorFrom(from:Point):Vector = Vector(from, this)
fun Point.toVector():Vector = Vector(x, y)
fun viewAngle(circle: Circle, from: Point): Angle{
    return (circle.radius / distance(circle.center, from)).let{sin -> Angle(sin, sqrt(1 - sin * sin))}
}
fun isBlocked(edge:Edge, circle: Circle):Boolean {
    if (cos(edge.a.toVectorFrom(edge.b), edge.a.toVectorFrom(circle.center)) <= viewAngle(circle, edge.a).cos + 0.000000001) return false
    return cos(edge.a.toVectorFrom(edge.b), edge.a.toVectorFrom(circle.center)) >= 0 &&
            cos(edge.b.toVectorFrom(edge.a), edge.b.toVectorFrom(circle.center)) >= 0
}
fun dot(a:Vector, b:Vector):Double {
    return a.x * b.x + a.y * b.y
}
fun cos(a:Vector, b:Vector):Double {
    return dot(a, b) / a.length / b.length
}
fun tangentialLine(point:Point, circle: Circle):Iterable<Line> {
    val viewA = viewAngle(circle, point)
    val angle = Line(point, circle.center).inclination
    return listOf(Line(point, angle + viewA), Line(point, angle - viewA))
}
fun minDistanceByCircle(a:Pair<Point, Iterable<Line>>, b:Pair<Point, Iterable<Line>>, circles:Iterable<Circle>):Double? {
    val (ap, aLines) = a
    val (bp, bLines) = b
    return aLines.flatMap { aLine ->
        bLines.mapNotNull {bLine ->
            Line.crossPoint(aLine, bLine) }
    }.filterNot{cp ->
        circles.any{isBlocked(Edge(ap, cp), it) || isBlocked(Edge(bp, cp), it)}
    }.map{cp ->
        distance(ap, cp) + distance(bp, cp)
    }.min()
}
fun main(args:Array<String>):Unit{
    val n = readLine()!!.trim().toInt()
    val circles = List(2){
        val (x, y, r) = readLine()!!.trim().split(' ').map(String::toDouble)
        Circle(Point(x, y), r)
    }
    val red = Array(n){
        val (x, y) = readLine()!!.trim().split(' ').map(String::toDouble)
        Point(x, y)
    }.map{p -> Pair(p, circles.flatMap { tangentialLine(p, it)})}
    val blue = Array(n){
        val (x, y) = readLine()!!.trim().split(' ').map(String::toDouble)
        Point(x, y)
    }.map{p -> Pair(p, circles.flatMap{tangentialLine(p, it)})}
    val minCost = searchMinCostMatch(red.map{r -> blue.map{b -> if (circles.any{isBlocked(Edge(r.first, b.first), it)}) minDistanceByCircle(r, b, circles) else distance(r.first, b.first)}.toTypedArray()}.toTypedArray())
    println(when(minCost){
        null -> "Impossible"
        else -> minCost
    })
}
fun searchMinCostMatch(edges:Array<Array<Double?>>):Double? {   //edges[red][blue] => redとblueの最短距離. なければnull
    val n = edges.size  //|Red| == |Blue| == n
    val rPotentialRed = Array(n){0.0}    //potentialRed[red] => redノードのポテンシャル
    val rPotentialBlue = Array(n){b -> edges.mapNotNull{it[b]}.min() ?: return null} //potentialBlue[blue] => blueノードのポテンシャル
    val rMatchingRed = Array(n){-1} //rMatchingRed[red] => redとマッチングしているblueがあればblue. そうでなければ-1
    val rMatchingBlue = Array(n){-1} //rMatchingBlue[blue] => blueとマッチングしているredがあればred.　そうでなければ-1
    val dMinRed = Array(n){Double.MAX_VALUE}    //dijkstra法で最短パスのコスト
    val dMinBlue = Array(n){Double.MAX_VALUE}   //dijkstra法で最短パスのコスト
    val dPrevRed = Array(n){-1}  //dPrevRed[red] => dijkstra法で最短パスの前のノードを保持. -1の場合s
    val dPrevBlue = Array(n){-1}  //dPrevBlue[blue] => dijkstra法で最短パスの前のノードを保持. -1の場合最短パスなし
    val isRenewRed = Array(n){false}
    var rTPotential = 0.0
    repeat(n){
        dMinRed.fill(Double.MAX_VALUE)
        dMinBlue.fill(Double.MAX_VALUE)
        dPrevRed.fill(-1)
        dPrevBlue.fill(-1)
        rMatchingRed.forEachIndexed { red, blue ->
            if (blue == -1) {
                dMinRed[red] = 0.0
                isRenewRed[red] = true
            }
        }
        var tMinCost = Double.MAX_VALUE
        var tIsRenew = false
        var minPath = -1
        while(isRenewRed.any{it} || tIsRenew){
            val (red, cost) = dMinRed.withIndex().filter{isRenewRed[it.index]}.minBy { it.value } ?: IndexedValue(-1, tMinCost)
            tIsRenew = false
            isRenewRed.fill(false)
            if (red == -1 || dMinRed[red] > tMinCost){
                rMatchingBlue.forEachIndexed { blue, nextRed ->
                    if (nextRed != -1){
                        if (dMinBlue[blue] > rTPotential - rPotentialBlue[blue] + tMinCost){
                            dMinBlue[blue] = rTPotential - rPotentialBlue[blue] + tMinCost
                            if (dMinRed[nextRed] > rPotentialBlue[blue] + edges[nextRed][blue]!! - rPotentialRed[nextRed] + dMinBlue[blue]){
                                dMinRed[nextRed] = rPotentialBlue[blue] + edges[nextRed][blue]!! - rPotentialRed[nextRed] + dMinBlue[blue]
                                isRenewRed[nextRed] = true
                            }
                        }
                    }
                }
            }
            else {
                edges[red].forEachIndexed { blue, d ->
                    if (d != null) {
                        if (dMinBlue[blue] > rPotentialRed[red] + d - rPotentialBlue[blue] + cost) {
                            dMinBlue[blue] = rPotentialRed[red] + d - rPotentialBlue[blue] + cost
                            dPrevBlue[blue] = red
                            if (tMinCost > dMinBlue[blue] + rPotentialBlue[blue]){
                                tMinCost = dMinBlue[blue] + rPotentialBlue[blue]
                                tIsRenew = true
                                minPath = blue
                            }
                            if (rMatchingBlue[blue] != -1) {
                                val nextRed = rMatchingBlue[blue]
                                if (dMinRed[nextRed] > rPotentialBlue[blue] + edges[nextRed][blue]!! - rPotentialRed[nextRed] + dMinBlue[blue]) {
                                    dMinRed[nextRed] = rPotentialBlue[blue] + edges[nextRed][blue]!! - rPotentialRed[nextRed] + dMinBlue[blue]
                                    dPrevRed[nextRed] = blue
                                    isRenewRed[nextRed] = true
                                }
                            }
                        }
                    }
                }
            }
        }
        if (minPath == -1) return null
        var blue = minPath
        while(blue != -1){
            val nextBlue = rMatchingRed[dPrevBlue[blue]]
            rMatchingBlue[blue] = dPrevBlue[blue]
            rMatchingRed[dPrevBlue[blue]] = blue
            blue = nextBlue
        }
        for (i in 0 until n){
            rPotentialRed[i] += dMinRed[i]
            rPotentialBlue[i] += dMinBlue[i]
        }
        rTPotential = tMinCost
    }
    return rMatchingRed.mapIndexed { red, blue -> edges[red][blue]!! }.sum()
}