[hihoCoder1163]博弈游戏·Nim游戏[博弈论]

题目链接:[hihoCoder1163]博弈游戏·Nim游戏

题意分析:额。。。

解题思路:原题给的提示:

这一次我们讲的是一个古老而又经典的博弈问题:Nim游戏。

Nim游戏是经典的公平组合游戏(ICG),对于ICG游戏我们有如下定义:
1、两名选手;
2、两名选手轮流行动,每一次行动可以在有限合法操作集合中选择一个;
3、游戏的任何一种可能的局面(position),合法操作集合只取决于这个局面本身;局面的改变称为“移动”(move)。
4、若轮到某位选手时,该选手的合法操作集合为空,则这名选手判负。

对于第三条,我们有更进一步的定义Position,我们将Position分为两类:
P-position:在当前的局面下,先手必败。
N-position:在当前的局面下,先手必胜。

他们有如下性质:
1.合法操作集合为空的局面是P-position;
2.可以移动到P-position的局面是N-position;
3.所有移动都只能到N-position的局面是P-position。

在这个游戏中,我们已经知道A[] = {0,0,...,0}的局面是P局面,那么我们可以通过反向枚举来推导出所有的可能局面,总共的状态数量为A[1]*A[2]*...*A[N]。并且每一次的状态转移很多。
虽然耗时巨大,但确实是一个可行方法。

当然,我们这里会讲这个题目就说明肯定没那么复杂。没错,对于这个游戏有一个非常神奇的结论:

对于一个局面,当且仅当A[1] xor A[2] xor ... xor A[N] = 0时,该局面为P局面。

对于这个结论的证明如下:
1. 全0状态为P局面,即A[i]=0,则A[1] xor A[2] xor ... xor A[N] = 0。
2. 从任意一个A[1] xor A[2] xor ... xor A[N] = k != 0的状态可以移动到A[1] xor A[2] xor ... xor A[N] = 0的状态。由于xor计算的特殊性,我们知道一定有一个A[i]最高位与k最高位的1是相同的,那么必然有A[i] xor k < A[i]的,所以我们可以通过改变A[i]的值为A[i]',使得A[1] xor A[2] xor ... xor A[i]' xor ... xor A[N] = 0。
3. 对于任意一个局面,若A[1] xor A[2] xor ... xor A[N] = 0,则不存在任何一个移动可以使得新的局面A[1] xor A[2] xor ... xor A[N] = 0。由于xor计算的特殊性,我们可以知道,一定是存在偶数个1时该位置的1才会被消除。若只改变一个A[i],无论如何都会使得1的数量发生变化,从而导致A[1] xor A[2] xor ... xor A[N] != 0。
以上三条满足ICG游戏中N,P局面的转移性质,所以该结论的正确性也得到了证明。

个人感受:在这里说说对证明中的2,3的理解。这里相当于存在两种情况:先手Alice碰到了1.异或和为0的情况,此时他要么不能做任何操作变成P局面导致失败,要么就是操作后使异或和变成非0,当变成非0后,机智的Bob小小一最优操作又使局面变成0了,如此循环,Alice作为先手必败!2.Alice碰到了异或和为非0的情况,机智的她就把情况变为0,此时Bob面临Alice面临的第一种情况,于是就有了答案。

具体代码如下:

#include <iostream>
using namespace std;

int main() {
    ios_base::sync_with_stdio(0);
    int a[111];
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) cin >> a[i];
    int ans = a[0];
    for (int i = 1; i < n; ++i) ans ^= a[i];
    if (ans)
        cout << "Alice\n";
    else cout << "Bob\n";
    return 0;
}

十佳代码top1:

#include <cstdio>
int N,A,X;
int main(){
    for(scanf("%d",&N),X=0;N--;scanf("%d",&A),X^=A);
    puts(X?"Alice":"Bob");
}


你可能感兴趣的:(博弈论)