問題概要
ある数字ゲームの勝者を求める問題。参加者は Louise と Richard の二名。下記のルールに従い勝者を判断する関数を実装する。なお最初のターンプレイヤーは Louise である。
- n は 0 を含まない自然数である。始めに任意の n が与えられる。
- ターンプレイヤーは n が 2 の冪であれば、n に n/2 を代入する。
- ターンプレイヤーは n が 2 の冪でなければ、n に min(n−2m) を代入する。
- n が 1 であれば、現ターンプレイヤーの勝利である。
- n が 1 でなければ、ターンプレイヤーを変更し 2. に戻る。
解答
Python 3
def counterGame(n):
c = sum([b == "1" for b in bin(n-1)[2:]])
if c & 1:
return "Louise"
else:
return "Richard"
解説
n が 2 の冪である場合、ターンごとに n は次表のように更新される。n=21 のときのターンプレイヤーが勝者であり、m が奇数であれば Louise が、偶数であれば Rechard が勝者となる。
turnLouiseRechard⋮⋮? (winner)n2m2m−1⋮2221(n)21000⋯000100⋯000⋮10010(1)
n が 2 の冪ではない場合、ターンごとに n は次表のように更新される。途中で 2 の冪 2m2 が得られ、 2m2 だけに限れば (1) より m2 の偶奇から勝者を推測できる。最終的な勝者は n が 2m2 となるまでのターンの切り替えを考慮しなければならない。次表より、m1 が奇数の場合はターンが切り替わり、m1 が偶数の場合はターンが元に戻ることが確認できる。よって m1+m2 が奇数のときは Louise が、偶数のとき Rechard が勝者となる。
turnLouiseRechard⋮⋮?nnmin(n−2k1)⋮min(n−∑i∈[m1]2ki)2m2(n)21010⋯110010⋯1100⋮1100100(2)
(1),(2) より、m と m1+m2 の偶奇により勝者を判断できる。しかし n の二進数表記から m,m1+m2 の値を得ようとすると、数える対象が {0,1} と混在しているため手間がかかる。そこで n−1 の二進数表記 1 の出現回数を数えることで、 m,m1+m2 の値の数え上げを容易にする。
n=2mn=2m(n)210:m000⋯00001:m11010⋯110:m200(n−1)201:m111⋯11111:m11010⋯101:m211
参考文献