ABC 151 F - Enclose All
問題
与えられた平面上の $n$ 個の点について、最小包含円の半径を求めよ。($n\leq50$)
解説
$n = 2$ の場合は自明なので、$n\geq 3$ の場合で考える。
$n$ 点の最小包含円はある $3$ 点の最小包含円になっているので、すべての $3$ 点の最小包含円を求めて、その最小包含円が残りの点を含むか調べればよい。
$3$ 点の最小包含円は、それらが鋭角三角形をなす場合はその外接円であり、鈍角三角形の場合は最長の辺を直径とする円になる。(直角三角形の場合はこれらは一致する)
この解法は計算量 $O(n ^ 4)$ であるが、$n\leq50$ なので問題ない。
実装
外心の座標の求め方については以下を参照。
中心 $p$、半径 $r$ の円に点 $q$ が含まれるための条件は $\| p - q \| ^ 2 \leq r ^2$ であるが、数値誤差が入るため、十分小さい $\varepsilon$ を用いて $\| p - q \| ^ 2 \leq r ^2 + \varepsilon$ としている。
def dist2(a, b): # 2点a,bの距離の2乗 ax, ay = a bx, by = b return (ax-bx)**2+(ay-by)**2 def circum(a, b, c): # 三角形ABCの外心 ax, ay = a bx, by = b cx, cy = c s = dist2(b, c)*(dist2(c, a)+dist2(a, b)-dist2(b, c)) t = dist2(c, a)*(dist2(a, b)+dist2(b, c)-dist2(c, a)) u = dist2(a, b)*(dist2(b, c)+dist2(c, a)-dist2(a, b)) ox = (s*ax+t*bx+u*cx)/(s+t+u) oy = (s*ay+t*by+u*cy)/(s+t+u) return ox, oy n = int(input()) P = [tuple(map(int, input().split())) for i in range(n)] eps = 10**(-7) if n == 2: r = dist2(P[0], P[1])**0.5 / 2 print(r) exit() for i in range(n-2): for j in range(i+1, n-1): for k in range(j+1, n): a, b, c = P[i], P[j], P[k] ax, ay = a bx, by = b cx, cy = c if dist2(a, b) + dist2(b, c) <= dist2(c, a): p = (cx+ax)/2, (cy+ay)/2 r2 = dist2(a, p) elif dist2(b, c) + dist2(c, a) <= dist2(a, b): p = (ax+bx)/2, (ay+by)/2 r2 = dist2(b, p) elif dist2(c, a) + dist2(a, b) <= dist2(b, c): p = (bx+cx)/2, (by+cy)/2 r2 = dist2(c, p) else: p = circum(a, b, c) r2 = dist2(a, p) if all([dist2(p, q) <= r2 + eps for q in P]): print(r2**0.5) exit()