ABC174E
ABC174E
二分探索だとは気付いて、実装したのだが、微妙なズレを「これは単純な二分探索ではダメなのに違いない」と思い込んで時間を使い切ってしまった
強い人のコードと見比べてみると、僕のコードには2つのバグがあった
どうすれば気づけただろうか
コンテスト中の思考
まず1本の場合を考える
それぞれの丸太についてk本に分割した場合の長さを求めて、kの和がKになるようにDP…
これは無理
k方向に10^9あるから
ならば逆転の発想で刻む長さの方に注目しよう
自力でこの発想に至ったのはナイス
本数を定義域として長さを値域とする構図を反転させる
刻む長さを定義域として刻まれる本数を値域にする
長さが短くなるほど本数は増える
→二分探索で目的の値を求めることができる
二分探索
left = 1
right = min(AS)
s = sum(a // x - 1 for a in AS)
s >= K
right = min(AS)
誤り
一番短い丸太を切ることなくK回の切断ができる場合がある
right = max(AS)が正しい
この場合切断可能数がゼロになる
left = 1
誤り
長さ1で刻んでもK回に至らないことがある
left = 0にしたが、今思えばこの辺りから少し考察がぬるかった
s = sum(a // x - 1 for a in AS)
誤り
right = max(AS)にしたことで発覚したが、xがaより大きい時にマイナスになってしまう
安易に条件分岐でパッチを当てた
sum(a // x - 1 if a >= x else 0 for a in AS)
誤り
コンテスト終了まで気づかなかったがここが一番のミス
「10からは3を3つ取れる」正しい
「だから最長3の時には10は3分割される、切断数は2」間違い
3.3になってる、切り上げたら4
10の時は3回切る、9の時は2回
これを踏まえると sum((a - 1) // x for a in AS) が正解
この図描けば分かったはずなのに…
s >= K
誤り
切り上げた値を返せという問題条件から、一致する値がない時には右側を返す必要がある
つまり一致する時には、それが右側に入る必要がある
sがK以上の時leftに入れるコードは誤り、s > K が正解
まとめ
二分探索の4つの構成要素をすべて間違えていた、うち2つは自力でコンテスト中に修正できたが、残り2つ残ってる状態で「これは単純な二分探索では解けないに違いない」と考え出して迷走した