【BZOJ1022】[SHOI2008] 小约翰的游戏(Anti-Nim)
大致题意: 与普通\(Nim\)游戏相比,改为拿到最后一个石子的人输,求谁有必胜策略。
前言
为做一道博弈论的题目开始恶补博弈论。。。
感觉现在理解能力不太行,就这么简单一道题看题解还看了半天。。。
分类讨论
对于这道题,我们需要分三类进行讨论:
- 每堆只有一个石子。显然此时每人每次必然是将一堆取走,因此偶数堆时先手赢,奇数堆时后手赢。若用\(SG\)函数表示,则\(SG=0\)时先手赢,\(SG>0\)时后手赢。
- 只有一堆石子个数大于\(1\)。则假如除此之外还剩奇数堆石子(个数都为\(1\)),先手可以把这堆石子取完;假如除此之外还剩偶数堆石子,先手可以留下\(1\)个石子。两种方式都把局面转化成了第一种情况里奇数堆的情况,因此先手(后手的后手)就必胜了。若用\(SG\)函数表示,显然一个大于\(1\)的数和若干\(1\)的异或值大于\(0\),因此\(SG>0\)时先手赢。
- 有大于一堆石子个数大于\(1\),即一般情况。考虑在不断取石子的过程中,必然有某一刻第三类情况被转化为了第二类情况(注意,不可能直接跳过第二类转化为第一类情况),而第二类情况相当于是\(SG>0\)时获胜。因此,二人都需要尽可能使自己的\(SG\)值大于\(0\),于是此时这个问题就变成了普通\(Nim\)游戏:\(SG>0\)时先手赢,\(SG=0\)时后手赢。
综上所述,先手赢的条件为每堆只有一个石子时\(SG=0\),或存在石子个数大于\(1\)的堆时\(SG>0\)。
代码
#include<bits/stdc++.h>
#define Tp tmeplate<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
using namespace std;
int n;
int main()
{
RI Tt,i,x,SG,cnt;scanf("%d",&Tt);W(Tt--)
{
for(SG=cnt=0,scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),SG^=x,x>1&&++cnt;//统计SG值,cnt记录1的个数
puts((cnt?SG:!SG)?"John":"Brother");//若有大于1的堆,则SG>0时先手胜;若无大于1的堆,则SG=0时先手胜
}return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒