尼姆博弈:
博主之所以要写这么一篇题解,是因为在算法课上做过的一道题.解题代码非常简单,但是博主愣是想了两天还没想明白其中的原理,直到今天才终于恍然大悟,特此记录下来分享给大家.看完了,想必你一定会懂!
题目如下:
题目描述
Here is a simple game. In this game, there are several piles of matches and two players. The two player play in turn. In each turn, one can choose a pile and take away arbitrary number of matches from the pile (Of course the number of matches, which is taken away, cannot be zero and cannot be larger than the number of matches in the chosen pile). If after a player’s turn, there is no match left, the player is the winner. Suppose that the two players are all very clear. Your job is to tell whether the player who plays first can win the game or not.
输入
The input consists of several lines, and in each line there is a test case. At the beginning of a line, there is an integer M (1 <= M <=20), which is the number of piles. Then comes M positive integers, which are not larger than 10000000. These M integers represent the number of matches in each pile.
输出
For each test case, output “Yes” in a single line, if the player who play first will win, otherwise output “No”.
简单翻译一下就是:
有两个玩家在玩取火柴的游戏,一共有N堆火柴,每个玩家每次至少取一根火柴,至多可以把一堆火柴全部拿走.问最后谁能够取光最后一根火柴,谁就是赢家.
这就是一个典型的尼姆博弈模型
尼姆博弈模型,大致上是这样的:
有3堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取1个,多者不限(但不能超过一堆),最后取光者得胜。
简单分析一下,我们使用(a, b, c)来表示某种局势.
- 我们很容易就想到,这个游戏的终局就是(0,0,0),不管谁面对这个局势,谁就输了.
- 其次,我们也能够想到一种十分浅显的必败局势(0,n,n),谁面对了这种局势,那么谁必败.
为什么?我们想一想,在这种局势下,不管你一次选择取多少,只要对方模仿自己在另一堆里面做同样的操作,那么最后一定是对方拿光最后一根火柴. - 对于最常见的(a, b, c)局势,我们应该怎么判断呢?
我们想到的当然是尽可能由自己将其构造成为(0, n, n)的形式,使对手面对(0, n, n)的局势,那么自己就必胜了.
对于第三点,也就是尼姆博弈问题的难点.到底应该怎么判断究竟哪一方能够尽可能快的构造出(0, n, n)的局势留给对方呢?
这个问题不知被哪个天才解决了,他将这个问题和二进制联系了起来(惊讶).至于他怎么想出来的我们就不得而知了,我们只解释一下原理.
首先我们将所有数写为二进制形式,然后依次使用异或运算(XOR)得到它们的异或和,我们称为Nim 和,
如果a XOR b XOR c = 0. 那么称此状态为平衡态,不管谁面对平衡态,那么对手必胜.(注意:题目假设两个玩家都是很聪明的,都能够做到最佳下法.)
如果a XOR b XOR c != 0. 那么称此状态为非平衡态,谁面对了非平衡态,就能够采取策略获胜.
举个例子:
(3,4,5).首先我们将其写为二进制形式,然后求出Nim和,发现不等于0,所以先手必胜.
我们来模拟一下过程:
这样一直下去,最后总会达到(0, n, n)的局面.
同理我们可以将问题推广到n个数.
我们要记住我们之前提到的,我们最终目的就是得到(0, n, n),这个问题推广之后也是一样的,我们需要得到(0, 0, 0 , 0…, n, n);
如果Nim和为0, 我们称之为平衡态(P态),我们可以通过策略使得平衡态最终转移到只有两堆相等的剩下的局面,故我们只需要使多堆石子保持平衡态,那么我们就可以最终获胜.
Bouton定理:先手能够在非平衡尼姆博弈中取胜,而后手能够在平衡的尼姆博弈中取胜。
证明:如果每堆都为0,显然是P状态(必败)。下面验证P状态和N状态(普通状态)的后两个递推关系:
一、每个N状态都可以一步到达P状态。
证明是构造性的。检查Nim和X的二进制表示中最左边一个1,则随便挑一个该位为1的物品堆Y,根据Nim和进行调整(0变1,1变0)即可。
例如Nim和为100101011,而其中有一堆为101110001。为了让Nim和变为0,只需要让操作的物品数取操作前的物品数和Nim的异或即可,可验证一下
显然操作后物品数变小,因此是合法的。设操作前其他堆的Nim和为Z,则有Y xor Z = X。操作后的Nim和为X xor Y xor Z = X xor X = 0,是一个P状态
二、每个P状态(必败态)都不可以一步再次到达P状态
由于只能改变一堆的物品,不管修改它的哪一位,Nim的对应位一定不为0,不可能是P状态。
这样就证明了Bouton定理:,最后有两堆相同的石子的情况必然为平衡态,而平衡态最终也会转换为两堆相同的石子的情况,故我们只需要保证多堆石子始终处于平衡态,我们便可以最终取得胜利。
最终结论:对手无法直接通过平衡态到达下一个平衡态,并且无论对手将状态引向何种非平衡态,我们总能将其纠正回平衡态.这样一直取下去,最终一定是由我们构造出(0,0,0,0…n,n)的平衡态,从而获胜.
最终答案很简单:
#include<iostream>
using namespace std;
int main()
{int n, a;while(scanf("%d", &n)!=EOF){int ans = 0;for(int i=0; i<n; ++i){scanf("%d", &a);ans ^= a;}if(ans) printf("Yes\n");else printf("No\n");}system("pause");return 0;
}
对尼姆博弈的总结就到这里了,欢迎大家访问我的个人博客:乔治的编程小屋,和我一起体验一下养成式程序员的成长之路吧!