POJ 2068 Nim (AOJ 1230, BOJ 9157)

http://poj.org/problem?id=2068 https://onlinejudge.u-aizu.ac.jp/challenges/search/titles/1230 https://www.acmicpc.net/problem/9157

https://vjudge.net/problem/POJ-2068 https://vjudge.net/problem/Aizu-1230 https://vjudge.net/problem/Baekjoon-9157

問題概要

(面倒なので0-indexedにしている)

  • 以下の形式で入力が与えられる
    • $N\ S\ M_0\ M_1\ \cdots\ M_{2N-1}$
  • チームA,BがNimを行う。最初、山には石が$S$個ある。
  • $i\ (\geq 0)$手目では、
    • $i$が奇数の場合チームAが、1個以上$M_{i\bmod{2N}}$個以下の石を取り除く
    • $i$が偶数の場合チームBが、1個以上$M_{i\bmod{2N}}$個以下の石を取り除く
  • 最後の1個の石を取り除いたチームの負けである(普通と逆なのに注意)
  • チームAが勝利戦略を持つか判定せよ

制約

  • $1\leq n\leq 10$
  • $1\leq M_i \leq 16$
  • $1\leq S\leq 2^{13}$

解法メモ

(現在誰の手番か, 現在の石の数)を持ってメモ化再帰すればよい

実装例

#include <iostream>
#include <vector>
#define rep(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
using namespace std;

int n, s;
vector<int> m;
vector<vector<int> > memo;
bool rec(int turn, int num) {
    if (num == 1) return false;
    if (memo[turn][num] != -1) return memo[turn][num];

    for (int i = 1; i <= m[turn]; ++i) {
        if (num - i <= 0) break;
        if (!rec((turn + 1) % (2 * n), num - i)) return memo[turn][num] = true;
    }
    return memo[turn][num] = false;
}

int main() {
    while (true) {
        cin >> n;
        if (n == 0) break;
        cin >> s;
        m.resize(2 * n);
        rep(i, 2 * n) cin >> m[i];

        memo.assign(2 * n, vector<int>(s + 1, -1));
        cout << rec(0, s) << "\n";
    }
}