这题思路十分有意思呢(也应该算是本题唯一难点)。
B和G(Bob和Georgia直接简称掉了)发明了一种游戏,在一个一维空间上一些点上有棋子,每次你可以将一枚棋子向左移动但不能跨越另一枚棋子,直到谁不能下了谁就输了,G先开始,给你局势,问谁必赢(不确定的情况显然是不存在的就不提了。为什么?后面会说的。)
模拟么???当然不是,这种鬼题怎么可能用模拟,这种题有博弈论的偏向的吧。
于是你就发现了,这题思路好难想的说。。。那就小数据模拟试试?
假设你现在只剩下最后一颗棋,那先手就必赢了。
假设剩下两颗棋,此时前一个人能动右边的棋子肯定不会动左边的棋子,因为你动了左边的棋子,对手就可以用右边的棋子使你到最后不得不把左边的棋子最左,所以如果两颗棋子相邻,后手赢,两颗棋子不相邻,先手赢。
假设剩下三颗棋,这样讨论就有点麻烦了,但我们不妨可以看做相当于一颗棋子加两颗棋子的情况,如果右边的两颗棋子相邻,先手肯定会把最左边的棋移到最左来造就上面的情况使自己赢,然后再多模拟几组样例,你会发现,答案和你最左边的棋到最左的距离是否等于你右边两颗棋的距离有关,如果相等的话先手是必输的(这已经开始用博弈思想了,你看三颗棋就已经这么麻烦了,你怎么可能用模拟呢)
类似的,四颗棋看成两组两颗棋做,五颗棋看成221来做…(像分治吧)这时候从最右边开始行动,当有人不能再动了,就会开始移动左端的棋子。
这样后我们看到,相当于把奇数和偶数分成了两种情况来做,而由三颗棋的做法,我们不难看出奇数的情况相当于在结束点再放了一颗棋子来做的,于是就可以把棋子分为一对一对来做,这时候如果你已经锁定了战局,对手不管怎么移动,你都可以想出方法继续锁定战局,而锁定战局的条件就是使两颗棋子相邻且这时候要对手走了(相当于对手只能移左边的棋子,你只要用右边的棋子逼住就好了)。
于是乎,你就相当于一对对棋子想办法使它们相邻(缩小距离),转化模型,想到博弈论一个经典模型——NIM博弈(两个人玩游戏,有n堆石子,每次操作可以选一堆石子拿,最少拿一颗,无子可取者输),或者说是其中的阶梯博弈(因为这种博弈通常情况可通过两两之间的状态决定胜负局面,如果总堆数为偶数,那就把1和0绑住,然后看对方怎么动,自己也相应采取策略继续锁定局面,也相当于以两堆石头的差值为新的堆来做NIM博弈),然后你就可以过了。
emmm,再证一下这个?
为了方便起见,就不完全说了(当然我底下还是写了一点),想要看的甩你们一个大佬的博客链接( https://www.cnblogs.com/nanjoqin/p/10211576.html ),或者算阶上也有的说,可以去详细了解一下(其实我这里就介绍个定理什么的就好,另外自己去看吧)。
$Mex$ 运算:$mex(S)=min_{x\in N,x\not\in S}${$x$}。
$SG$ 函数:设当前状态 $x$ 有 $k$ 个后继情况($y_1,y_2,···,y_k$),那么 $SG(x)=mex${$SG(y_1),SG(y_2),···,SG(y_k)$},从而 $SG(G)=SG(s)$(G为整个游戏,s为起点) ,当且仅当它为0是先手必输。
$SG$ 定理:游戏的总和的 $SG$ 值是各个子游戏的 $SG$ 值的 $xor$。
于是就相当于你列举每一对棋子间的空格数(每堆石子个数),把它们异或起来判断即可(或者说直接一个阶梯模板)。
哦对了,这题还有个坑点,它的输入没有排好序。。。
好了,上代码(这题解打的有点小累啊。。。讲的有点繁琐了。。。但貌似其实还可以讲很多???)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
int t,n,fl,p[maxn];
void freo(){
freopen("GB.in" ,"r",stdin );
freopen("GB.out","w",stdout);
}
void prep(){
scanf("%d",&t);
}
void init(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&p[i]);
}
void work(){
fl=0;
if(n&1) p[++n]=0;
sort(p+1,p+n+1);
for(int i=2;i<=n;i+=2)
fl^=p[i]-p[i-1]-1;
}
void prin(){
if(0) printf("Not sure\n");//这是不可能的,所以来搞笑一波
else if(fl) printf("Georgia will win\n");
else printf("Bob will win\n");
}
int main(){
freo();
prep();
while(t--){
init();
work();
prin();
}
return 0;
}
这题使我NIM博弈入门了。。。不过这题码量真的挺小的,而且这题在博弈论里算比较简单的题吧。
为以防万一,我这里再写点博弈论的东西。。。
对于和这题一些博弈论简单介绍一下:
简单博弈:
Bash博弈:
一堆石子每次可取1到n个,无子可取者为负,问必胜策略
N状态(先手必胜)和P状态(先手必输):
其实这里先手指的是当前的人啦,所以每个P状态都是从上一个N状态转移过来的,反之亦然,这样逆推来解决Bash博弈
SG函数:
Mex运算:
mex(S)=min_{x属于N,x不属于S}{x}
设当前状态x有k个后继情况(y_1,y_2,···,y_k),那么SG(x)=mex{SG(y_1),SG(y_2),···,SG(y_k)},SG(G)=SG(s)(G为整个游戏,s为起点)
SG函数主要利用了其定义以判断当前的胜负状态。往往最终局面的SG函数值被设为0,此时是个P状态,不为0时即为N状态
若任意SG(v)≠0则SG(u)=0,对应了P状态的后继状态都是N状态;若存在SG(v)=0则SG(u)≠0,对应了一个N状态总有一个后继状态是P状态(u为当前局面,v为后续局面)
NIM博弈:
两个人玩游戏有n堆石子,每次操作可以选一堆石子拿,最少拿一颗,无子可取者为负
引入一个SG定理:
游戏的总和的SG值是各个子游戏的SG值的xor
证明自己看算阶。。。
于是就可以吧NIM博弈变为n个Bash博弈,当前状态为SG(x)=x,所以答案的SG值即为各个堆石子数的异或值
阶梯博弈:
NIM博弈一种变异,通常情况下只有两两之间的状态可以决定局面的胜负,然后就可以进行捆绑来做,奇数时1和0绑
为什么把a[n]-a[n-1]记为第一级台阶的石子,而不是a[i]-a[0]?
因为它把每个棋子像左移动实际上是可以理解为左边的那堆石子分给右边的,也就是如果按正序记录是第i堆分给第i+1堆,而在一般的阶梯博弈里是第i+1堆分给第i堆,所以为了方便直接倒序存储转换成一般的阶梯博弈的情况
巨佬