【BZOJ1022】[SHOI2008] 小约翰的游戏(Anti-Nim)

点此看题面

大致题意: 与普通(Nim)游戏相比,改为拿到最后一个石子的人输,求谁有必胜策略。

前言

为做一道博弈论的题目开始恶补博弈论。。。

感觉现在理解能力不太行,就这么简单一道题看题解还看了半天。。。

分类讨论

对于这道题,我们需要分三类进行讨论:

  1. 每堆只有一个石子。显然此时每人每次必然是将一堆取走,因此偶数堆时先手赢,奇数堆时后手赢。若用(SG)函数表示,则(SG=0)时先手赢,(SG>0)时后手赢。
  2. 只有一堆石子个数大于(1)。则假如除此之外还剩奇数堆石子(个数都为(1)),先手可以把这堆石子取完;假如除此之外还剩偶数堆石子,先手可以留下(1)个石子。两种方式都把局面转化成了第一种情况里奇数堆的情况,因此先手(后手的后手)就必胜了。若用(SG)函数表示,显然一个大于(1)的数和若干(1)的异或值大于(0),因此(SG>0)时先手赢。
  3. 有大于一堆石子个数大于(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;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1022.html