Official
C - 丸太の分割 / Splitting Logs Editorial by admin
DeepSeek V3(貪欲の正当性の証明なし)概要
丸太を\(K\)箇所で切断して\(K+1\)個の断片に分割するとき、断片の長さの最大値\(M\)を最小化する問題です。
考察
問題を直接解こうとすると、\(N-1\)箇所から\(K\)箇所を選ぶ組み合わせを考える必要があり、計算量が非常に大きくなります。そこで、二分探索を用いて「最大値\(M\)を\(X\)以下にできるか」という判定問題に変換します。\(X\)が大きすぎると切断回数が少なくて済み、\(X\)が小さすぎると多くの切断が必要になります。この性質を利用して、条件を満たす最小の\(X\)を二分探索で見つけます。
アルゴリズム
- 二分探索の範囲を設定します。左端\(left\)は最大の部品の長さ(1つの部品は分割できないため)、右端\(right\)はすべての部品の長さの合計とします。
- 各ステップで\(mid = (left + right) // 2\)を計算し、最大断片長を\(mid\)以下にできるか判定します。
- 判定方法:左から順に部品を加算していき、合計が\(mid\)を超えた時点で切断し、切断回数をカウントします。最終的な切断回数が\(K\)以下(断片数が\(K+1\)以下)なら、\(mid\)は達成可能です。
- 達成可能なら\(right = mid\)、不可能なら\(left = mid + 1\)として探索範囲を狭め、\(left = right\)になるまで繰り返します。
計算量
- 時間計算量: \(O(N \log(\sum A_i))\)
- 二分探索の反復回数は\(O(\log(\sum A_i))\)回
- 各判定は\(O(N)\)で行う
- 空間計算量: \(O(N)\)
- 入力配列を保存するため
実装のポイント
入力読み込みは
sys.stdin.readで一括読み込みし、高速化しています。二分探索の終了条件は
left < rightで、最終的にleftが答えになります。判定時に注意する点:最後の断片もカウントするため、ループ後に
count += 1が必要です。切断回数と断片数の関係:\(K\)回切断すると\(K+1\)個の断片ができるため、判定条件は
count <= k + 1となります。ソースコード
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:2+n]))
left = max(A)
right = sum(A)
while left < right:
mid = (left + right) // 2
count = 0
current = 0
for a in A:
if current + a > mid:
count += 1
current = a
else:
current += a
count += 1
if count <= k + 1:
right = mid
else:
left = mid + 1
print(left)
if __name__ == "__main__":
main()
この解説は deepseekv3 によって生成されました。
posted:
last update: