文章目录
一、ICG
1.Nim游戏属于ICG
Nim游戏是博弈论中最经典的模型(之一),它又有着十分简单的规则和无比优美的结论 。Nim游戏是组合游戏(Combinatorial Games)的一种,准确来说,属于“Impartial Combinatorial Games”(以下简称ICG)。
2.ICG的条件
满足以下条件的游戏是ICG:
1、有两名选手;
2、两名选手交替对游戏进行移动(move),每次一步,选手可以在(一般而言)有限的合法移动集合中任选一种进行移动;
3、对于游戏的任何一种可能的局面,合法的移动集合只取决于这个局面本身,不取决于轮到哪名选手操作、以前的任何操作或者其它什么因素;
4、如果轮到某名选手移动,且这个局面的合法的移动集合为空(也就是说此时无法进行移动),则这名选手负。根据这个定义,很多日常的游戏并非ICG。
例如象棋就不满足条件3,因为红方只能移动红子,黑方只能移动黑子,合法的移动集合取决于轮到哪名选手操作。
二、Nim游戏的定义
通常的Nim游戏的定义是这样的:
-
有若干堆石子,每堆石子的数量都是有限的。
-
两个玩家交替进行游戏。
-
当轮到每个玩家取子时,选择这些堆中的一堆,并从所选的堆中取走至少一枚硬币(可以取走他所选堆中的全部硬币)。
-
最后取完的玩家即为胜者。(即不能取子的玩家输)
三、过程实例
1.一堆石子
如果轮到你的时候,只剩下一堆石子,那么此时的必胜策略肯定是把这堆石子全部拿完一颗也不给对手剩,然后对手就输了。
2.两堆石子
如果剩下两堆不相等的石子,必胜策略是通过取多的一堆的石子将两堆石子变得相等,以后如果对手在某一堆里拿若干颗,你就可以在另一堆中拿同样多的颗数,直至胜利。
如果你面对的是两堆相等的石子,那么此时你是没有任何必胜策略的,反而对手可以遵循上面的策略保证必胜。
四、分析
1.规则
定义
P-position(先手必败)和N-position(后手必败),其中P代表Previous,N代表Next。
规则:
- 无法进行任何移动的局面(terminal position)是P-position.
- 可以移动到P-position的局面是N-position.(只要找到一个是P-position的子局面就能说明是N-position)
- 所有移动都导致N-position的局面是P-position。(所有的子局面都是N-position,它才是P-position)
解析
-
意思是p为必输的局面,这是无法再进行移动
-
意思是如果当前的局面你如果可以胜利,那么必然意味着你能创造一个必输的局面给对方
-
意思是如果你是必输的局面,那么意味着不管比赛有没有分出最后的结果,你的所有移动得到的结果都是对方胜利的局面。或者说都是对方又可以把你置为必输的局面的位置
2.dp(记忆化搜索)
(1)原理
比如说我刚才说当只有两堆石子且两堆石子数量相等时后手有必胜策略,也就是这是一个P-position,下面我们依靠定义证明一下(3,3)是一个P-position。
首先(3,3)的子局面(也就是通过合法移动可以导致的局面)有(0,3)(1,3)(2,3)(显然交换石子堆的位置不影响其性质,所以把(x,y)和(y,x)看成同一种局面)。
-
(0,3)的子局面有(0,0)、(0,1)、(0,2),其中(0,0)显然是P-position,所以(0,3)是N-position(只要找到一个是P-position的子局面就能说明是N-position)。
-
(1,3)的后继中(1,1)是P-position(因为(1,1)的唯一子局面(0,1)是N-position),所以(1,3)也是N-position。
-
同样可以证明(2,3)是N-position。
所以(3,3)的所有子局面都是N-position,它就是P-position。通过一点简单的数学归纳,可以严格的证明“有两堆石子时的局面是P-position当且仅当这两堆石子的数目相等”。
根据上面这个过程,可以得到一个递归的算法——对于当前的局面,递归计算它的所有子局面的性质,如果存在某个子局面是P-position,那么向这个子局面的移动就是必胜策略。当然,可能你已经敏锐地看出有大量的重叠子问题,所以可以用DP或者记忆化搜索的方法以提高效率。
(2)适用条件
数据范围很小,可以记忆化搜索所有可能的状态
利用这个算法,对于某个Nim游戏的局面(a1,a2,…,an)来说,要想判断它的性质以及找出必胜策略,需要计算O(a1a2…*an)个局面的性质,当数据很多时,时间复杂度会很高。
3.Bouton’s Theorem
(1)内容
Bouton’s Theorem:对于一个Nim游戏的局面(a1,a2,…,an),使用异或运算判断胜负。
对于先手玩家,
如果a1a2…^an!=0→先手玩家必胜(N-position)
如果a1a2…^an =0→先手玩家必败(P-position)
(2)证明
根据定义,证明一种判断position的性质的方法的正确性,只需证明其是否符合规则:
第一个规则
显然,terminal position只有一个,就是全0,异或仍然是0。
第二个规则
将其(N-position必然有一个P-positon子局面)表示出来:对于某个局面(a1,a2,…,an),若a1a2…an!=0(N-postion),一定存在某个合法的移动,将ai改变成ai’后满足a1a2…ai’…an=0(P-position)。
不妨设a1a2…an=k,则一定存在某个ai,它的二进制表示在k的最高位上肯定是1(意思是比如k=4,用二进制表示是100,ai是5,101)。这时aik<ai一定成立(001<101)。则我们可以将ai改变成ai’=aik(ai’<ai,意思是取走了某堆中的若干子),此时a1a2…ai’…an=a1a2…ank=0=k^k=0。
第三个规则
对于某个局面(a1,a2,…,an),若a1a2…an=0,一定不存在某个合法的移动,将ai改变成ai’后满足a1a2…ai’…an=0。因为异或运算满足消去率,由a1a2…an=a1a2…ai’…an可以得到ai=ai’。所以将ai改变成ai’不是一个合法的移动。证毕。
根据这个定理,我们可以在O(n)的时间内判断一个Nim的局面的性质,且如果它是N-position,也可以在O(n)的时间内找到所有的必胜策略。Nim问题就这样基本上完美的解决了。
(3)算法
int N,A[MAX_N];
void solve()
{
int x=0;
for(int i=0;i<N;i++) x^=A[i];
if(x!=0) cout<<"先手者获胜"<<endl;
else cout<<"后手者获胜"<<endl;
}
PS:0^k=k
五、例题
#include<iostream>
using namespace std;
#define MAX_N 1000
int N,A[MAX_N];
void solve()
{
int x=0;
for(int i=0;i<N;i++) x^=A[i];
if(x!=0) cout<<"A"<<endl;
else cout<<"B"<<endl;
}
int main()
{
cin>>N;
for(int i=0;i<N;i++) cin>>A[i];
solve();
return 0;
}