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
";
    } else {
        cout << "Yes
";
        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();
    }
}

原文地址:https://www.cnblogs.com/Xiao-yan/p/14191621.html