Official

H - Remove Pairs Editorial by MtSaka


場にあるカードの集合としてありえるものは \(2^{N}\) 通りです。 この全ての場合について、それが先手が操作をする直前の時に場にあるカードの集合であったときに先手が勝つか後手が勝つか判定することができれば良いです。

これはいわゆる bitDP というテクニックを利用して解くことができます。
\(dp[S]\) = 場にあるカードの集合が \(S\) の状態で先手から始まる時に先手が勝つことができる時 true そうでない時 false
とし、遷移は以下の通りです。

カードの集合 \(S\) について、ある \(i,j \in S\)\(A_i=A_j\) または \(B_i=B_j\) を満たす \(i,j\) について \(dp[S \setminus \{i,j\}]\)falseである時に \(dp[S]\)true、それ以外の場合では \(dp[S]\)false

最終的に答えは、\(S=\{1,2,\ldots,N\}\) の時の \(dp[S]\)trueである時に Takahashifalseである時に Aokiとなります。

状態数は \(O(2^{N})\) で遷移は \(O(N^{2})\) であるため、計算量は \(O(2^{N} {N}^{2})\) となり、十分高速です。

実装例(C++):

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; ++i)
        cin >> a[i] >> b[i];
    vector<int> dp(1 << n, -1);
    dp[0] = 0;
    for (int i = 1; i < (1 << n); ++i) {
        bool f = false;
        for (int j = 0; j < n; ++j)
            for (int k = j + 1; k < n; ++k) {
                if (((i >> j) & 1) && ((i >> k) & 1)) {
                    if ((a[j] == a[k] || b[j] == b[k]) && dp[i ^ (1 << j) ^ (1 << k)] == 0) {
                        f = true;
                    }
                }
            }
        dp[i] = f;
    }
    cout << (dp.back() ? "Takahashi" : "Aoki") << endl;
}

posted:
last update: