Branched Evolution

Competitive Programming in Python

ABC 151 F - Enclose All

問題

与えられた平面上の $n$ 個の点について、最小包含円の半径を求めよ。($n\leq50$)

atcoder.jp

解説

$n = 2$ の場合は自明なので、$n\geq 3$ の場合で考える。
$n$ 点の最小包含円はある $3$ 点の最小包含円になっているので、すべての $3$ 点の最小包含円を求めて、その最小包含円が残りの点を含むか調べればよい。
$3$ 点の最小包含円は、それらが鋭角三角形をなす場合はその外接円であり、鈍角三角形の場合は最長の辺を直径とする円になる。(直角三角形の場合はこれらは一致する)
この解法は計算量 $O(n ^ 4)$ であるが、$n\leq50$ なので問題ない。

実装

外心の座標の求め方については以下を参照。

ja.wikipedia.org

中心 $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()

atcoder.jp