B - 荷物の配送センター / Package Distribution Center Editorial by admin Editorial for printing
Claude 4.6 Opus (Thinking)概要
\(N\) 個の荷物それぞれに対して、\(M\) 箇所の配送センターの中から最も近いものを選び、距離の合計を最小化する問題です。各荷物は独立に最寄りの配送センターを選べばよいため、二分探索を用いて効率的に解けます。
考察
重要な気づき:各荷物は独立に最適な配送センターを選べる
求める値は \(\sum_{i=1}^{N} |D_i - S_{f(i)}|\) の最小値です。この式をよく見ると、各荷物 \(i\) の項 \(|D_i - S_{f(i)}|\) は他の荷物の割り当てに一切影響を受けません(1つの配送センターが複数の荷物を担当してもよいため)。したがって、各荷物について個別に最も近い配送センターを選べばよいのです。
素朴なアプローチの問題点
各荷物 \(i\) に対して全配送センター \(j\) を調べて \(|D_i - S_j|\) の最小値を求めると、計算量は \(O(N \times M)\) です。\(N, M\) がともに最大 \(2 \times 10^5\) の場合、\(4 \times 10^{10}\) 回の計算が必要となり、制限時間に間に合いません(TLE)。
解決策:ソート+二分探索
配送センターの座標をソートしておけば、各荷物の届け先 \(D_i\) に最も近い配送センターを二分探索で \(O(\log M)\) で見つけられます。
具体例で説明します。配送センターが \(S = [1, 5, 8]\)(ソート済み)で、届け先が \(D_i = 6\) の場合:
- 二分探索で \(6\) の挿入位置を求めると \(\text{pos} = 2\)(\(S[2] = 8\) が \(6\) 以上の最小の要素)
- 右側の候補:\(S[2] = 8\) → 距離 \(|6 - 8| = 2\)
- 左側の候補:\(S[1] = 5\) → 距離 \(|6 - 5| = 1\)
- 最小は \(1\) なので、配送センター \(5\) を選ぶ
アルゴリズム
- 配送センターの座標 \(S\) をソートする。
- 各荷物の届け先 \(D_i\) について以下を行う:
bisect_leftで \(D_i\) のソート済み配列 \(S\) における挿入位置 \(\text{pos}\) を求める。- \(S[\text{pos}]\)(\(D_i\) 以上で最小の値)と \(S[\text{pos}-1]\)(\(D_i\) 未満で最大の値)の2つが最寄り候補。
- 境界チェック(\(\text{pos} = 0\) や \(\text{pos} = M\) の場合)をした上で、距離が小さい方を選ぶ。
- 各荷物の最小距離を合計して出力する。
計算量
- 時間計算量: \(O(M \log M + N \log M)\)
- 配送センターのソートに \(O(M \log M)\)、各荷物に対する二分探索に \(O(\log M)\) × \(N\) 回
- 空間計算量: \(O(N + M)\)
- 入力データの格納に必要な分
実装のポイント
境界チェックが重要: 二分探索で得た挿入位置 \(\text{pos}\) が \(0\) の場合は左側の候補がなく、\(M\) の場合は右側の候補がありません。両方のケースを正しく処理する必要があります。
高速入出力: \(N, M\) が大きいため、Python では
sys.stdin.buffer.read()でまとめて読み込むことで入力を高速化しています。距離の計算の工夫: \(S\) はソート済みなので、\(S[\text{pos}] \geq D_i\) かつ \(S[\text{pos}-1] < D_i\) が成り立ちます。そのため絶対値を取らずに
S[pos] - dとd - S[pos-1]で距離を計算でき、わずかに効率的です。ソースコード
import bisect
import sys
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
D = [int(input_data[idx + i]) for i in range(N)]; idx += N
S = [int(input_data[idx + i]) for i in range(M)]; idx += M
S.sort()
total = 0
for d in D:
pos = bisect.bisect_left(S, d)
best = float('inf')
if pos < M:
best = min(best, S[pos] - d)
if pos > 0:
best = min(best, d - S[pos - 1])
total += best
print(total)
main()
この解説は claude4.6opus-thinking によって生成されました。
posted:
last update: