Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで塔壊しゲームの勝者を見つけるプログラム

高さを表す配列 height が与えられているとします。ここには高さの異なる n 本の塔があり、Amal(アマル)と Bimal(ビマル)の2人が次のルールでゲームを行います。

  • 必ず Amal が先手です。
  • 各手番で、現在のプレイヤーは高さ X の塔を1本選び、それを高さ Z の塔 Y 本に分解します(ただし Y × Z = X、かつ X と Y はどちらも 1 より大きい整数)。
  • 自分の手番で動かせる塔がなくなったプレイヤーの負けとなります。

このゲームの勝者の名前を求めるのが課題です。

例えば入力が height = [3,1,2] の場合、答えは Bimal になります。初期状態は {3, 1, 2} です。Amal が高さ 2 の塔を高さ 1 の塔2本に分解すると、塔の構成は {3, 1, 1, 1} になります。そこで Bimal が高さ 3 の塔を高さ 1 の塔3本に分解すれば、Amal はもう動かせる塔がなくなり、勝者は Bimal となります。

解き方:スプラーグ=グランディの定理

この種のゲームはスプラーグ=グランディ(Sprague–Grundy)の定理を使うと効率的に解けます。各塔の Grundy 数(ニム数)を事前計算しておき、すべての塔の Grundy 数の XOR を取ります。結果が 0 以外なら先手(Amal)の勝ち、0 なら後手(Bimal)の勝ちです。

アルゴリズムの手順

  1. util() 関数を定義します。引数 limit の初期値は 10^3 + 5 です。
  2. result := サイズ limit の配列を用意し、すべて 0 で初期化します。
  3. i を 2 から limit − 1 まで順に処理します。
    • s := 新しい空の集合
    • j を 1 から √i の切り捨て値まで繰り返します。
      • d := i ÷ j の商、r := i ÷ j の余り
      • r が 0 の場合(j が i の約数の場合):
        • j が奇数なら、result[d] を s に追加
        • d が奇数なら、result[j] を s に追加
    • j := 0 とし、j が s に存在する間 j を 1 ずつ増やします。
    • result[i] := j(s に含まれない最小の非負整数:mex)
  4. result を返します。
  5. g := util() として Grundy 数のテーブルを作成します。
  6. メイン処理では以下を実行します。
    • r := 0
    • height の各要素 i について:r := r XOR g[i]
    • r が 0 以外なら "Amal" を返し、そうでなければ "Bimal" を返します。

実装例

理解を深めるために、以下の実装を見てみましょう。

def util(limit=10**3+5):
    result = [0] * limit

    for i in range(2, limit):
        s = set()
        for j in range(1, int(i**0.5)+1):
            d, r = divmod(i, j)

            if r == 0:
                if j & 1:
                    s.add(result[d])
                if d & 1:
                    s.add(result[j])

        j = 0
        while j in s: j += 1
        result[i] = j

    return result

g = util()

def solve(height):
    r = 0

    for i in height:
        r ^= g[i]

    if r:
        return "Amal"
    else:
        return "Bimal"

height = [3,1,2]
print(solve(height))

入力

[3,1,2]

出力

Bimal
  1. Pythonで多角形の外周(周囲長)を求めるプログラム

    問題の概要2次元平面上にある単純な多角形(自己交差しないポリゴン)の頂点が、順序付きの点のリストとして与えられているとします。このとき、その多角形の外周(周囲長)を求めることが目的です。例として、入力が points = [(0, 0), (0,5), (3, 5), (3,0)] の場合を考えてみましょう。このときの出力は 16 になります。これは、図からも分かるように、長さ3の辺が2本、長さ5の辺が2本存在するためです。したがって、2×5 + 2×3 = 16 となります。アルゴリズムの考え方この問題は、「隣接する2つの頂点間の距離をすべて計算して合計する」というシンプルなアプローチで解く

  2. Pythonで制約付きの建物の最大高さを求めるプログラム

    問題の概要整数 n と制約リスト restrictions が与えられたとします。私たちは都市に n 棟の新しい建物を一列に建てようとしていますが、高さに関するいくつかの制限があります。建物には左から順に 1 から n までの番号が付けられており、各制約は restrictions[i] = (id_i, max_height_i) の形式で表され、「id_i 番の建物の高さは max_height_i 以下でなければならない」ことを意味します。建物の高さに関する都市の規則は以下のとおりです。各建物の高さは 0 以上でなければなりません。1 番の建物(最初の建物)の高さは必ず 0 です。隣接す