B - ロープの収納 / Storing the Rope 解説 by admin
Claude 4.6 Opus (Thinking)概要
長さ \(L\) のロープを半分に折りたたむ操作(切り上げ除算)を繰り返し、長さ \(W\) 以下にするために必要な最小回数を求める問題です。
考察
問題の整理
ロープの長さが \(x\) のとき、1回折りたたむと長さは \(\lceil x/2 \rceil\) になります。これを繰り返して \(W\) 以下にしたいです。
具体例: \(L = 11\), \(W = 3\) の場合
| 回数 | 長さ |
|---|---|
| 0回 | 11 |
| 1回 | \(\lceil 11/2 \rceil = 6\) |
| 2回 | \(\lceil 6/2 \rceil = 3\) |
2回で \(W = 3\) 以下になるので、答えは 2 です。
素朴なアプローチで問題ないか?
一見すると \(L\) が最大 \(10^{18}\) なので心配になりますが、1回の操作で長さがおよそ半分になるため、ループ回数は最大でも \(\log_2(10^{18}) \approx 60\) 回程度です。これは十分高速なので、単純にシミュレーションするだけで解けます。
切り上げ除算の実現
\(\lceil x/2 \rceil\) を整数演算で計算するには、\((x + 1) \div 2\)(切り捨て除算)を使います。
- \(x = 11\)(奇数): \((11 + 1) / 2 = 6\) ✓(\(\lceil 11/2 \rceil = 6\))
- \(x = 6\)(偶数): \((6 + 1) / 2 = 3\) ✓(\(\lceil 6/2 \rceil = 3\))
一般に、整数 \(x\) を整数 \(d\) で割った切り上げは \(\lceil x/d \rceil = \lfloor (x + d - 1) / d \rfloor\) で求められます。\(d = 2\) のとき \((x + 1) // 2\) となります。
アルゴリズム
- カウンタ
countを \(0\) に初期化する。 - \(L > W\) である間、以下を繰り返す:
- \(L\) を \(\lceil L/2 \rceil = (L + 1) // 2\) に更新する。
countを \(1\) 増やす。
countを出力する。
L, W = map(int, input().split())
count = 0
while L > W:
L = (L + 1) // 2
count += 1
print(count)
計算量
- 時間計算量: \(O(\log L)\)(毎回長さがおよそ半分になるため、ループは最大約 \(60\) 回)
- 空間計算量: \(O(1)\)
実装のポイント
切り上げ除算の書き方:
(L + 1) // 2で \(\lceil L/2 \rceil\) を正しく計算できます。Python では//が切り捨て除算(正の数に対して)なので、分子に \(d - 1 = 1\) を足すテクニックが使えます。最初から条件を満たす場合: \(L \leq W\) のとき
whileループに入らず、count = 0がそのまま出力されるため、特別な場合分けは不要です。多倍長整数: \(L\) が最大 \(10^{18}\) ですが、Python は整数のオーバーフローがないため、そのまま扱えます。C++ などを使う場合は
long long型を使う必要があります。ソースコード
L, W = map(int, input().split())
count = 0
while L > W:
L = (L + 1) // 2
count += 1
print(count)
この解説は claude4.6opus-thinking によって生成されました。
投稿日時:
最終更新: