nim博弈题

取(m堆)石子游戏 HDU - 2176 vj

题意概述:给出n堆石子,nim博弈判谁能赢,并且如果先手获胜输出所有先手获胜的第一步方案。

复习了一下\(nim\)博弈.
判赢的\(nim\)异或和\(nim = a_{1} \oplus a_{2} \oplus a_{3} \oplus a_{4}\oplus \cdots \oplus a_{n}\)

如果\(nim\)和为\(0\)先手必败
否则,我们看这样一组数

0001
0010
0100
1000
\(nim\)和为1111显然,所以先手必赢,那么先手如何赢?就是通过一个操作,让下一步该对面先手时,处于必败态。什么操作?就是让下一步对面面临着nim和为0,

首先要知道\(a \oplus b \oplus a = b\)所以可让\(t = nim \oplus a_{i}\)这样再判\(t\)是否小于\(a_{i}\),如果小于,那麽\(t\)一定可以从\(a_{i}\)取出来,如果不能,说明这堆不能操作,得找其他堆进行操作

我还想胡乱证明一下为什么nim和大于0,一定能找到一种方式,使得该对方先手时,nim积为0。
因为\(nim\)一定存在一个最高位,这是必然的,那么造成\(nim\)的这个最高位\(a\),他一定是也有这个最高位的,如果\(t = nim \oplus a_{i}\)那么\(t\)一定小于\(a\),也就是一定存在方案。

#include <iostream>

using namespace std;
int cas = 0;
int m;
int a[200099];
void solve() {
    for (int i = 1; i <= m; i++) 
        cin >> a[i];
    int nim = 0;
    for (int i = 1; i <= m; i++) 
        nim ^= a[i];
    if (nim == 0) {
        cout << "No\n";
    } else {
        cout << "Yes\n";
        for (int i = 1; i <= m; i++) {
            int Nim = nim^a[i];
            if (Nim < a[i]) {
                cout << a[i] << " " << Nim << endl;
            }
        }
    }
}
int main()
{
    while (cin >> m) {
        if (m == 0)return 0;
        solve();
    }
}

posted @ 2020-12-26 10:03  u_yan  阅读(101)  评论(0)    收藏  举报