Problem:
E - Disappear Drive / 消えるドライヴ

Story

Dのひとはバスケットボールが好きだが、シュートが好きなため、他のテクニックはからっきしである。ドリブルは特に苦手で、相手の守備範囲に入ると必ずボールを奪われてしまう。そこでどんな相手も必ず抜き去る必殺のドリブルを編み出すことにした。努力の末、彼は遂に消えるドライヴ、"ディスアピアドライヴ"を完成させた。

ディスアピアドライヴは、相手を挑発することで集中力を奪い視線を外す"ディスディレクション"により隙を作り、その隙に次元(ディメンジョン)の壁を超えて相手の守備範囲をすり抜けてしまう完全無欠のドライヴである。しかし、次元の壁を超えることは体への負担が大きいため、次元を超えられる回数には制限がある。また、常に集中力を使うため、通常の移動を含め、方向転換が1度までしかできない。コート上でどのように動けば、ボールを奪われずに最短でゴールまで辿り着けるだろうか。

Problem

二次元平面上に、(0,0)を左下、(50,94)を右上とする長方形を考える。また、この平面上にはN個の円が存在し、i番目の円の中心の位置は(x_i,y_i)、半径はr_iである。円同士に重なりはない。

(25,0)を点S、(25,94)を点Gとし、SからGへ至る経路を考える。「経路」とは、(1)SとGを結んだ線分であるか、(2)長方形内部の任意の点Pについて線分SPと線分GPを結合したものからなる。SからGへと向かう経路の途中、一度ある円の内部に入ってからその円の内部から出るまでの間を「円の内部を通過する区間」と定義する。ただし、長方形・円の周上はそれぞれ長方形・円の内部には含まれないとする。

円の内部を通過する区間の数がD個以下であるような経路のうち、最短の経路の長さを求めよ。どうしてもゴールできないときは-1を返すこと。

Input

入力は以下の形式からなる。

N D
x_1 y_1 r_1
...
x_N y_N r_N

1行目は2つの整数からなり、円の個数N、円の内部を通過できる回数Dが空白1文字区切りで与えられる。 続くN行は円の情報が与えられる。 i+1行目(1 \leq i \leq N)は3つの整数からなり、i番目の円の中心のx座標x_i、y座標y_i、半径r_iが空白1文字区切りで与えられる。

制約:

0 \leq N \leq 5

0 \leq D \leq 5

0 \leq x_i \leq 50

0 \leq y_i \leq 94

1 \leq r_i \leq 100

任意の2円間は10^{-5}以上離れていると仮定してよい。

S,Gはそれぞれ任意の円の内部には含まれないと仮定してよい。

S,Gはそれぞれ任意の円と10^{-5}以上離れていると仮定してよい。

最短経路における点Pは、長方形の周および任意の円周から10^{-5}以上離れていると仮定してよい。

Output

最短経路の長さを1行に出力せよ。行の最後では必ず改行を行うこと。 ただし、どうしてもゴールできないときは-1を返すこと。 正答に対し、10^{-7}以下の絶対誤差、あるいは相対誤差は許容される。

Sample Input 1

1 0
25 47 10

Sample Output 1

96.2027355887

消えることができないので、迂回した経路が最短である。

Sample Input 2

1 1
25 47 10

Sample Output 2

94.0000000000

消えることができるので、一直線に突き抜ければよい。

Sample Input 3

1 0
20 47 5

Sample Output 3

94.0000000000

円周上は円の内部に含まれないので、消えずに通過することができる。

Sample Input 4

1 0
25 47 40

Sample Output 4

-1

消えることも迂回することもできないため、ゴールに到達できない。

Sample Input 5

5 2
11 10 16
33 40 18
20 66 10
45 79 14
22 85 8

Sample Output 5

96.1320937224

Solution in Scala (judge verdict: Accepted)

object Main extends App {
  import scala.collection.mutable
  import scala.io.StdIn._


  val tolerance = 0.0000001
  val Array(n, d) = readLine.trim.split(' ').map(_.toInt)
  val circles = Array.tabulate(n){_ ⇒
    val Array(x, y, r) = readLine.trim.split(' ').map(_.toInt)
    Circle(Point(x, y), r)
  }
  val start = Point(25, 0)
  val goal = Point(25, 94)
  val stage = Square(0, 50, 0, 94)
  if (countIntersections(start, start, goal, circles) <= d){
    println(start.distanceFrom(goal))
  }else {
    val lineS = circles.flatMap(c ⇒ c.tangentsFrom(start))
    val lineG = circles.flatMap(c ⇒ c.tangentsFrom(goal))
    val distances = for {a ← lineS
      b ← lineG
      mid ← a.crossPoint(b).filter(stage.contains)
      if countIntersections(start, mid, goal, circles) <= d
    } yield {
      start.distanceFrom(mid) + mid.distanceFrom(goal)
    }
    println(if (distances.isEmpty) -1 else distances.min)
  }

  case class Square(left: Int, right: Int, top: Int, bottom: Int) {
    def contains(point: Point): Boolean = left - tolerance < point.x && point.x < right + tolerance && top - tolerance < point.y && point.y < bottom + tolerance
  }
  def countIntersections(from: Point, mid: Point, to: Point, circles: Array[Circle]): Int = {
    val edgeA = Edge(from, mid)
    val edgeB = Edge(mid, to)
    circles.count(c ⇒ c.hasIntersection(edgeA)) + circles.count(c ⇒ c.hasIntersection(edgeB)) - (if (circles.exists(c ⇒ c.contains(mid))) 1 else 0)
  }
  case class Circle(center: Point, radius: Double) {
    def contains(point: Point): Boolean = center.distanceFrom(point) < radius + tolerance
    def tangentsFrom(point: Point): List[Line] = {
      val sin = radius / center.distanceFrom(point)
      val diff = Angle(Point(math.sqrt(1 - sin * sin), sin))
      val angle = Angle(Point(center.x - point.x, center.y - point.y))
      List(Line(point, angle + diff), Line(point, angle - diff))
    }
    def hasIntersection(edge: Edge): Boolean = {
      if (contains(edge.from) || contains(edge.to)) true
      else {
        val asLine = Line(edge.from, edge.to)
        if (asLine.distanceFrom(center) < radius - tolerance) {
          asLine.crossPoint(Line(center, Angle(asLine).rotate90)).exists(edge.onEdge)
        } else {
          false
        }
      }
    }
    //def crossPoints(edge: Edge): List[Point] = crossPoints(Line(edge.from, edge.to)).filter(p ⇒ edge.onEdge(p))
    //def crossPoints(line: Line): List[Point]
  }
  case class Edge(from: Point, to: Point) {
    def onEdge(point: Point): Boolean = (to.distanceFrom(point) < tolerance || (math.abs((from.x - to.x) * (point.y - to.y) - (from.y - to.y) * (point.x - to.x)) < tolerance)) && math.min(from.x, to.x) <= point.x && point.x <= math.max(from.x, to.x)
  }
  object Line{
    def apply(a: Point, b: Point): Line = {
      new Line(b.y - a.y, a.x - b.x, b.y * (b.x - a.x) + b.x * (a.y - b.y))
    }
    def apply(p: Point, angle: Angle): Line = {
      new Line(-angle.sin, angle.cos, p.x * angle.sin - p.y * angle.cos)
    }
  }
  case class Line(a: Double, b: Double, c: Double){
    private[this] def isParallel(that: Line): Boolean = {
      math.abs(a * that.b - b * that.a) < tolerance
    }
    def crossPoint(that: Line): Option[Point] = {
      if (isParallel(that)) None
      else Some(Point((b * that.c - that.b * c) / (a * that.b - that.a * b), (c * that.a - that.c * a) / (a * that.b - b * that.a)))
    }
    def distanceFrom(point: Point): Double = {
      math.abs(a * point.x + b * point.y + c) / math.sqrt(a * a + b * b)
    }
  }
  object Angle{
    def apply(line: Line): Angle = {
      val norm = math.sqrt(line.a * line.a + line.b * line.b)
      new Angle(-line.a / norm, line.b / norm)
    }
    def apply(point: Point): Angle = {
      val norm = math.sqrt(point.x * point.x + point.y * point.y)
      new Angle(point.y / norm, point.x / norm)
    }
  }
  case class Angle private (sin: Double, cos: Double) {
    def +(that: Angle): Angle = new Angle(sin * that.cos + cos * that.sin, cos * that.cos - sin * that.sin)
    def -(that: Angle): Angle = new Angle(sin * that.cos - cos * that.sin, cos * that.cos + sin * that.sin)
    def rotate90: Angle = new Angle(-cos, sin)
  }
  case class Point(x: Double, y: Double) {
    def distanceFrom(that: Point): Double = math.sqrt((x - that.x).let(it ⇒ it * it) + (y - that.y).let(it ⇒ it * it))
  }
  implicit class Scope[T](val value: T) extends AnyVal {
    @inline def also(@inline func: T ⇒ Unit): T = {
      func(value)
      value
    }
    @inline def let[A](@inline func: T ⇒ A): A = func(value)
  }
}