Day 077-Q3 — 三分探索 + 凸包上の二変数最適化(Golden Section Search・上包絡線最小値)

2026-06-30 赤色 Master / Phase 8+ ★★★★★★★★★ 三分探索・凸関数・上包絡線・Li Chao Tree

問題

平面上に $N$ 個の点 $P_1, \ldots, P_N$ がある。実数パラメータ $t \in [0, 1]$ を選び、以下の目的関数を最小化する $t$ と最小値を求めよ:

$$f(t) = \max_{1 \le i \le N} \left( t \cdot x_i + (1-t) \cdot y_i \right)$$

精度 $10^{-9}$ で答えよ。

制約

パラメータ範囲備考
$N$$2 \le N \le 2 \times 10^5$点の数
$x_i, y_i$$-10^9 \le x_i, y_i \le 10^9$各点の座標
精度$10^{-9}$絶対誤差または相対誤差

入出力例

入力例1

3
0 6
4 2
3 3

出力例1

3.500000000000

$f(t) = \max(6-6t,\; 2+2t,\; 3)$。直線 $6-6t$ と $2+2t$ の交点 $t=0.5$ で最小値 $3.5$(点 $(3,3)$ が切り上げを防ぐ)。実際の最小は $t=0.5$ で $f(0.5)=3.5$。

概念図: 上包絡線と三分探索

f(t) = max_i(t·x_i + (1-t)·y_i) の上包絡線(凸関数) t f(t) 0 1 6-6t (x=0,y=6) 2+2t (x=4,y=2) 3 (x=3,y=3) 上包絡線 f(t) t=0.5 3.5 三分探索の収束領域

各点 $P_i$ が定義する直線 $t(x_i-y_i)+y_i$ の上包絡線は凸関数。三分探索で最小値を $O(200N)$ で求める。

ヒント

ヒント1(方向性)

$f(t) = \max_i h_i(t)$ は線形関数の最大値なので凸関数。凸関数の最小化には三分探索が使える。

ヒント2(アプローチ)

各点 $P_i = (x_i, y_i)$ は直線 $h_i(t) = (x_i - y_i)t + y_i$ を定義する。$f(t) = \max_i h_i(t)$ の最小値を三分探索で求める。区間 $[0, 1]$ を 200 回繰り返すと精度 $(2/3)^{200} \approx 10^{-35}$ で十分。

ヒント3(ほぼ答え)
def f(t):
    return max(t * xi + (1 - t) * yi for xi, yi in points)

lo, hi = 0.0, 1.0
for _ in range(200):
    m1 = lo + (hi - lo) / 3.0
    m2 = hi - (hi - lo) / 3.0
    if f(m1) <= f(m2):
        hi = m2
    else:
        lo = m1

print(f"{f((lo + hi) / 2.0):.12f}")

模範解答

import sys
input = sys.stdin.readline

def solve():
    N = int(input())
    points = [tuple(map(int, input().split())) for _ in range(N)]

    def f(t):
        return max(t * xi + (1.0 - t) * yi for xi, yi in points)

    lo, hi = 0.0, 1.0
    for _ in range(300):
        m1 = lo + (hi - lo) / 3.0
        m2 = hi - (hi - lo) / 3.0
        if f(m1) <= f(m2):
            hi = m2
        else:
            lo = m1

    ans = f((lo + hi) / 2.0)
    print(f"{ans:.12f}")

solve()

Step-by-Step 解説

Step 1: 問題の変換

$f(t) = \max_i (t x_i + (1-t) y_i)$ を変形すると、各点は直線 $h_i(t) = (x_i - y_i) t + y_i$ を定義する。$f(t)$ はこれら $N$ 本の直線の上包絡線

Step 2: 凸関数の性質

線形関数の最大値は凸関数。証明: $f(\lambda t_1 + (1-\lambda) t_2) \le \lambda f(t_1) + (1-\lambda) f(t_2)$。

Step 3: 三分探索の区間縮小

各ステップで区間が $2/3$ 倍に縮む。200 回後: $(2/3)^{200} \approx 10^{-35}$。精度 $10^{-9}$ には 70 回でも十分。

Step 4: 計算量

フェーズ計算量
三分探索の評価回数$O(200)$
各評価$O(N)$
合計$O(200N)$

よくあるミス

ミス原因正しい書き方
<<= の混同平坦部のある関数での注意f(m1) <= f(m2)hi = m2
回数が少なすぎ精度不足200 回以上推奨
$t \in [0,1]$ 以外を探索問題の制約を無視lo=0.0, hi=1.0
整数型のまま計算浮動小数点誤差float への変換を明示

次のステップ

  • 発展問題: $f(t) = \max_i (a_i t^2 + b_i t + c_i)$(凸 2 次関数の上包絡線最小値)
  • 応用: 「三分探索 on 三分探索」で二変数の最適化 $f(t, s)$ が凸ならば適用可
  • 参考: Li Chao Tree による上包絡線の静的・動的管理 $O(\log C)$ per query

自己評価