C - 丸太の分割 / Splitting Logs Editorial by admin
Qwen3-Coder-480B(貪欲の正当性の証明なし)概要
与えられた丸太を \(K\) 箇所で切断し、分割後の断片の長さの最大値 \(M\) を最小化する問題。
考察
この問題では、丸太をいくつかの断片に分割するときに、「最も長い断片ができるだけ短くなる」ように切断する必要があります。素朴な方法として、全ての可能な切断の組み合わせを試すことを考えられますが、\(N\) が最大 \(2 \times 10^5\) であり、組み合わせは非常に多いので現実的ではありません(計算量的にTLEになります)。
重要な観察は、「断片の長さの最大値 \(M\) の最小値を求めたい」という最適化問題は、二分探索によって効率的に解けるということです。つまり、「ある値 \(x\) 以下の最大断片長で分割可能かどうか?」という判定問題を繰り返し解くことで、最小の \(M\) を求めることができます。
この判定問題自体は貪欲法で解けます。左から順に部品を足していき、次に足すと上限 \(x\) を超えるならその時点で切る、という操作を繰り返します。切る回数が \(K\) 回以内であればOK、そうでなければNGです。
このように、「最小化する値を二分探索で求め、条件判定は貪欲法で行う」という典型的なテクニックが使える問題です。
アルゴリズム
二分探索の範囲を設定
- 最小値:最も長い単一の部品(どの断片も少なくともこれ以上にはならない)
- 最大値:全ての部品の合計(全部つなげたもの)
二分探索を実施
midを「断片の最大長の上限」として、その値で分割可能か判定- 判定関数
is_possible(max_len)は、貪欲に断片を作っていき、必要な切断回数が \(K\) 以下かどうかを確認
貪欲な分割判定
- 左から累積和を取るように部品をグループにまとめていく
- 和が
max_lenを超えそうになったらそこで切る - 切った回数が \(K\) 以下ならOK
最終的な答えを出力
計算量
- 時間計算量: \(O(N \log(\text{sum}(A)))\)
- 二分探索の範囲は最大 \(10^{14}\) 程度なので、\(\log\) はせいぜい50程度
- 判定関数内で配列を1周する処理が \(O(N)\)
- 空間計算量: \(O(N)\)
- 入力配列の保持に \(O(N)\)
実装のポイント
二分探索の範囲を正しく設定すること(
left = max(A), right = sum(A))切断回数のカウントを正確に行うこと(最初の断片はカット不要)
入力を高速に読み込むため、
sys.stdin.readなどを用いると安心ソースコード
def main():
import sys
input = sys.stdin.read
data = input().split()
N = int(data[0])
K = int(data[1])
A = list(map(int, data[2:]))
# 二分探索で答えを求める
def is_possible(max_len):
# 各断片の長さがmax_len以下になるように切る
cuts = 0
current_sum = 0
for a in A:
if current_sum + a > max_len:
cuts += 1
current_sum = a
if cuts > K:
return False
else:
current_sum += a
return True
left = max(A)
right = sum(A)
while left < right:
mid = (left + right) // 2
if is_possible(mid):
right = mid
else:
left = mid + 1
print(left)
if __name__ == "__main__":
main()
この解説は qwen3-coder-480b によって生成されました。
posted:
last update: