题目大意:
Nim取子游戏是由两个人面对若干硬币(或石子,或。。。)进行的游戏,设有k>=1堆石子,各堆分别有n1,n2,......,nk枚硬币,游戏的目的就是选取最后剩下的硬币,游戏法则如下:
1:游戏人交替游戏(我们称第一个取子者为游戏人1,称第二个取子者为游戏人2)。
2:当轮到每个游戏人取子时,选择这些硬币堆中的一堆,并从所选的堆中取走至少一枚硬币(游戏人也可以取走他所选择的堆中全部的硬币,于是剩下一个空堆)。
当所有的堆都变成空堆的时候,游戏结束。最后取子(即能够取走最后一堆中剩下的所有硬币)的游戏人,视为游戏的得胜者。
解题思路:
首先我们了解两个状态:
P状态:是指先前刚操做完的玩家有必胜策略(P来源于Previous player wins)。
N状态:是指当前即将操作的玩家有必胜状态(N来源于Next player wins)。
简单的介绍一个定理吧:NP状态定理:P状态是“胜利终止状态”或者它的一切后继都为N状态,N状态是“失败终止状态”或拥有至少一个后继是P状态。意思就是说P状态的下一步都为N状态,N状态的下一步至少有一个P状态。
定理介绍完了,下面我们来说说题,这个游戏的中的变量是堆数k和各堆的硬币数n1,n2,......,nk。对应的组合问题是,确定游戏人1获胜还是游戏人2获胜以及游戏人应该如何取子才能保证获胜(获胜策略)。
为了进一步理解Nim取子游戏,我们考察某些特殊情况,如果游戏开始时只有一堆硬币,游戏人1则通过取走所有硬币获胜,现在设有2堆且它们分别含有n1,n2枚硬币。游戏人1能否获胜并不取决于n1,n2的具体值是多少,而是取决于它们是不是相等,设n1!=n2,游戏人1从大堆中取走足够数量的硬币使得两堆含有相同数目的硬币留给游戏人2去取。以后当轮到游戏人1取子时就模仿游戏人2 来取子。如果游戏人2从一堆中取走c枚硬币,那么游戏人1就从另一堆也取走c枚硬币。这样的策略保证游戏人1获胜。如果n1==n2,游戏人2则可以通过模仿游戏人1来取子而获胜,这样,我们就完全解决了2堆Nim取子游戏问题。
上述求解2堆Nim取子游戏的思想,即取子后使得留下2堆子数相等的方法,可以推广到任意k堆的情况,游戏人所需要的洞察力来自2为基的正整数概念。回忆一下,每个正整数n都可以通过反复减去不超过该数的2的最大幂而被表示出来。例如,57的二进制是1 1 1 0 0 1(57=2^5+2^4+2^3+2^0),我们可以认为每一堆硬币数由2的幂数的子堆组成,而二是该数的基。于是,57枚硬币组成的堆则是由大小为2^5,2^4,2^3,2^0的各子堆组成,在2堆Nim取子游戏中,各种大小的子堆数只能是0,1,2。具有特定大小的子堆恰好存在一个当且仅当游戏中的两堆具有不同的大小。换句话说,各种子堆的总堆数是偶数当且仅当游戏中2堆具有相同的大小,也就是说,游戏人2可以获胜。
现在考虑各堆大小分别为n1,n2, ...... ,nk的一般Nim取子游戏。将每一个数ni表示成二进制:
N1 = as…a1a0
N2 = bs…b1b0
……
Nk = ms…m1m0
(我们通过在前面补0的方法可以假设,所有各堆大小都是具有相同位数的二进制数)如果每一种大小的子堆的个数都是偶数,我们就称Nim取子游戏是平衡的。因此,Nim取子游戏是平衡的,当且仅当:
as + bs + … + ms 是偶数
……
a1 + b1 + … + m1 是偶数
a0 + b0 + … + m0是偶数
一个Nim取子游戏如果不是平衡的,就称为非平衡的,如果和数ai + bi + … + mi 是偶数,我们就说第i位是平衡的,否则就称为非平衡的。因此,一个平衡的取子游戏中则是至少存在一个非平衡位的游戏。于是,我们有:
游戏人1能够在非平衡的Nim取子游戏中取胜,而游戏人2能够在平衡Nim游戏中取胜。
为了理解上述结论,我们推广在2堆Nim取子游戏中所用的策略。设Nim取子游戏是非平衡的,令最大的非平衡位为第j位,于是,游戏人1用这样一种方法取子,使得留给游戏人2的游戏是平衡的取子游戏。具体的说,游戏人1选择第j位是1的堆,然后从所选的堆中取走一些硬币使得游戏成为平衡的游戏。此后,无论游戏人2怎么做,它留给游戏人1 的都是一个非平衡的东西,而游戏人1再次将游戏变为平衡状态。如果一直这样进行下去,则游戏人1必胜。如果取子游戏从平衡状态开始,那么游戏人1第一次取子后必将形成非平衡游戏,这时,只要游戏人2取子,他就采取使游戏平衡策略。
例如(敲了这么多字,累死我了......),考虑4堆Nim取子游戏,其中各堆大小分别为7,9,12和15枚硬币。这些堆的大小是写成二进制是0111,1001,1100和1111.用2的幂的子堆表示,我们有
23 = 8
22 = 4
21 = 2
20 = 1
大小为7的堆 0 1 1 1 大小为9的堆 1 0 0 1 大小为12的堆 1 1 0 0 大小为15的堆 1 1 1 1
这局游戏是非平衡的,其中第3号位(三次方),第二号位和第零号位都是非平衡的,游戏人1可以选择大小为12的堆并取走11枚硬币,只剩下1枚硬币,由于1的二进制是0001,所以取子游戏变为平衡状态。
23 = 8
22 = 4
21 = 2
20 = 1
大小为7的堆 0 1 1 1 大小为9的堆 1 0 0 1 大小为12的堆 0 0 0 1 大小为15的堆 1 1 1 1
同样的道理,游戏人1也可以选择大小为9的堆并取走5枚硬币而剩下4枚硬币,或者游戏人1还能选择大小为15的堆并取走13枚硬币而留下2枚。
总的来说,Nim博弈就是要掌握P状态和N状态(平衡,非平衡),以前做Nim博弈只知道异或,看了《组合数学》上的Nim才彻底搞懂!!!呼,打了一个多小时,累死我了。
前两天做南京理工大学的校赛,有一个反尼姆博弈的题目,其实懂了原理之后都一样,也是找这两个状态,当然,有大神用sg函数打表过得(标神),现在的我还不会sg打表(好弱的说------)。今晚南京理工大学爆了,oj进不去了,改天我找找那个题,然后再放到博客里来吧!
好累-_-@|||
更新!!!!
谁才是最强战舰!
Time Limit: 1000MS
Memory Limit: 65536KB
Description
依阿华来到镇守府的第一件事情,就是找大和solo!然而这并不是什么好消息,说不定,镇守府,甚至佐伯湾就这么消失了。。。于是,提督君想了一个简单的游戏,来分出她们的胜负。游戏规则如下:这里有N堆石子,每堆石子有a[i](1<=i<=N)个,每人轮流从其中的某一堆石子中拿出任意个石子(只能在其中一堆拿,不能不拿),大和先手,谁拿出了最后一个石子,谁输。若大和必胜,输出“Yamato_Saikou!”,若依阿华必胜,输出“Meidikeji_Shijiediyi!”,若两边都无法必胜,输出“Sayonara_Konosekai!”.
Input
第一行输入一个正整数T(1 <= T <= 1000),表示有T组测试数据。对于每组测试数据:第一行一个正整数,N(N<=1000),表示有N堆石子。第二行N个整数a[i](1<=a[i]<=1000),表示每堆石子的数量。
题目连接:https://icpc.njust.edu.cn/Contest/749/H/
我的代码,第一次敲Nim,大神们勿喷
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int i,n;
scanf("%d",&n);
int sum=0,h;
int ans=0;
for(i=0;i<n;i++)
{
scanf("%d",&h);
sum^=h;
if(h!=1)
ans++;
}
if((ans!=0&&sum)||(ans==0&&!sum))//第二种情况是每一堆的个数都是1但是有偶数堆的时候还是第一个人胜,仔细想一想很容易就通了
puts("Yamato_Saikou!");
else
puts("Meidikeji_Shijiediyi!");
}
return 0;
}