博弈(三) 尼姆博弈(Nimm Game)

尼姆博弈(Nimm Game)

有3堆任意多的物品(x, y, z)。两个人轮流拿,每次只能从一堆中拿,至少拿一个,至多不限。拿到最后者胜利。
结论:
必败点为 x^y^z = 0。(^为异或)
证明:
如果当前局势为(0,0,0)。先手肯定输。其中0^0^0=0
如果当前局势为(0,n,n)。先手肯定输。因为无论先手怎么拿,后手都可以从另一堆里拿相同多个。最终转化成(0,0,0)的情况。其中0^n^n=0
如果当前局势为(1,2,3)。先手肯定输。无论先手怎么拿,后手都可以将局势转为(0,n,n)的情况。其中1^2^3=0
以(1,2,3)为例:

10进制 二进制
1 01
2 10
3 11

当前为奇异局势当且仅当二进制的各位的和为偶数,称达到平衡态,达到平衡态的每一位称位平衡位。无论先手怎么拿,后手都可以通过一步操作将局势转为新的平衡态。所以x^y^z=0为奇异局势。先手必输。
将非奇异局势转化为奇异局势只需将z变成x^y即可。因为x^y^z = x^y^(x^y) = (x^x)^(y^y) = 0^0 = 0
任意堆的推广:
1堆的时候先手赢。x != 0
2堆的时候取决于两堆的数目是否相等,相等的话x^y=0,后手赢,不相等x^y!=0,先手赢
推广到任意多堆的情况(x1, x2, x3··· ···xn)
例如当n=4的时候,各堆大小分别是8,12,14,16

十进制 二进制
8 01000
12 01100
14 01110
16 10000

先手可以从16的堆里拿走6个使之成为平衡态

十进制 二进制
8 01000
12 01100
14 01110
10 01010

之后无论后手怎么拿,先手都可以使之重新成为平衡态。所以最后先手一定胜利。
递推关系的验证:
1.每一个n状态(必胜态)都可以通过一步转为p状态(必败态)
例如当前异或和X为1000101,总可以寻找到一个堆Y与X最高位的同位为1,例如Y=1001110,Z = X^Y=0001011,Z肯定小于Y,则可以将Y堆拿出(Y-Z)个物品,使得异或和为0达到平衡态。
设没操作之前的异或和为X,当前堆为Y,其他堆异或和为Z,即Z^Y=X,然后将当前堆改为Y^X。则操作之后的异或和等于Z^Y^X=X^X=0(p状态)
2.每个p状态不可能通过一步操作转为p状态
p状态异或和为0,如果只对一堆操作,无论拿多少个异或和都不可能为全0。
所以对于任意多堆时结论仍然成立。
例题:
HDU - 1850
代码:

#include<bits/stdc++.h>

using namespace std;
const int maxn = 100 + 5;
int a[maxn];
int n;

int main() {
    while(cin >> n, n) {
        int ans = 0;
        for(int i = 0; i < n; i++) {
            cin >> a[i];
            ans ^= a[i];
        }
        int cnt = 0;
        for(int i = 0; i < n; i++) {
            if(a[i] > (a[i]^ans)) cnt++;
        }
        cout << cnt << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/l1832876815/article/details/81120928