把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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;
}
posted @ 2020-05-18 08:47  TheLostWeak  阅读(118)  评论(0编辑  收藏  举报