【競プロ】特定文字列にするための操作回数を求めよ【Python3】
問題
答え
s = input()[::-1]
bt = 0
for i in range(len(s)):
abt = int(s[i]) - (bt % 10)
if abt < 0:
abt += 10
bt += abt
print(len(s) + bt)
解説
ボタンを押す回数は何回か?
最初から分かっているのはAボタンの回数。どんな数字列だったとしても桁数の分だけAボタンは必ず押す。よって計算するのはBボタンの回数だけでよい。
print(len(s) + bt)
# bt:Bボタンを押した数
どうすれば操作回数が分かる?
与えられた数字列に対して、入力者と逆の操作をする。右端から数字を小さくして0になったら取り除くという操作を行う。Aボタンの逆の処理をするボタンを逆Aボタンと呼ぶことにする。(Bも同様)
例)「21」だったとしたら
- すべての数字を1つ小さくする(逆B) 10
- 最後の0を取り除く(逆A) 1
- 1を0にする(逆B) 0
- 最後の0を取り除く(逆A)空文字(操作終了)
操作回数は4回。サンプルの出力例と一致する。
逆順ループは分かりづらいから数字列を逆転させる
というわけで数字列を右から処理したいが、分かりづらいので数字列の方を逆転させることにする。
s = input()[::-1] ## ←これで入力文字列が逆転するよ
for i in range(len(s)):
# ループの記述が簡単になる
逆Bボタンを押した回数を計算
# abt:その数字を0にするために逆Bボタンを押した回数
abt = int(s[i]) - (bt % 10)
if abt < 0:
abt += 10
数字が3だったら3回押す必要があるが、Bボタンは押すとすべての数字が変わってしまう。
のでこれまでに押された回数から、s[i]が今いくつになっているのか計算する必要がある。
Bボタンは10回押すと1周回って同じ数字になるため、10で割った余りを使えばよい。
abt = int(s[i]) - (bt % 10)
こうすると例えばこれまでに計14回押した場合、4回押した場合と同じ状態となることを表現できる。
が、これだとabtがマイナスになった時、正常に計算できない。
現在のループで2という数字を処理している時、ここまでに計4回ボタンを押していると実際には「8」になっているはずである。
このようにマイナスになった場合のみ10を足すと正しい数字に戻る。
# abt:その数字を0にするために逆Bボタンを押した回数
abt = int(s[i]) - (bt % 10)
if abt < 0:
abt += 10
同じ数字が続く時(ゾロ目)
同じ数字が続く時はBボタンを押す回数が節約できる。
この場合には、ループの中の以下の部分では
# abt:その数字を0にするために逆Bボタンを押した回数
abt = int(s[i]) - (bt % 10)
最初の1
の時は1回押したと計算されるが、2回目の1
の時は0回となる。ゾロ目の場合、最初の数字の分だけBボタンを押す回数がカウントされ、2回目以降は0回。
ゾロ目でも例外処理を付け加える必要はない。
ゾロ目に他の数字が1つくっついたケース
これは2つに分けて考えられる。
99
とは2
を取り除いた時点での残りの数字列の状態。11
と同じように計算するとBボタンは9回、Aボタンは2回。よって
つまりゾロ目+他の数字といった場合でもループの中身はそのままでよい。
for i in range(len(s)):
abt = int(s[i]) - (bt % 10)
if abt < 0:
abt += 10
bt += abt
帰納法的なアレでどんな数字列でもこのコードで操作回数が割り出せることが分かる。(分かれ)
Discussion