您的位置:首页 > 其它

用SJ定理解决Anti-SG游戏

2015-09-04 10:08 323 查看

Anti-SG游戏定义

1、决策集合为空的操作者胜。

2、其余规则与SG游戏一致。

SJ定理

对于任意一个Anti-SG游戏,如果定义所有子游戏的SG值为0时游戏结束,先手必胜的条件:

1、游戏的SG值为0且所有子游戏SG值均不超过1。

2、游戏的SG值不为0且至少一个子游戏SG值超过1。

证明

先证明第一个条件:

所有都不超过1,那么显然如果有偶数个1则先手必胜偶数个1即游戏的SG值为0。

再证明第二个条件:

如果有至少两个子游戏SG值超过1,然后其余有偶数个1。那么游戏的SG值就为所有SG值超过1的子游戏的SG值的Nim和。设为X。假设X中1的最高位为第k位,那么一定存在至少一个子游戏的SG值第k位有1,设这个子游戏的SG值为Y。那么我们可以把这个子游戏变为YxorX。可以知道YxorX<Y,因为会消去第k位上的1,k位之后不变,那么相当于减少了一个2k−1,前k-1位不论如何变,最多增加2k−1−1。根据SG函数的转移可以得知一定能转移到一个决策其SG值为YxorX。接下来游戏的SG值便变为0,而此时至少存在一个子游戏的SG值超过1。此条件下先手必胜。

如果有至少两个子游戏的SG值超过1,然后其余有奇数个1。我们设所有SG值超过1的子游戏的SG值的Nim和为X。那么游戏的SG值为Xxor1。因为不为0,那么现在如果X=0,我们去掉一个1即可。于是变为游戏的SG值为0,而此时至少存在两个子游戏的SG值超过1。如果X不为0,那么最高位的1一定不在第一位。于是和1没有关系,像上面一样处理即可。

如果只有一个子游戏的SG值超过1,然后其余有偶数个1。显然我们可以拿将那个超过1的子游戏转移到一个决策使SG值为1,就能造成奇数个1。

如果只有一个子游戏的SG值超过1,然后其余有奇数个1。直接将那个超过1的子游戏转移到一个决策使SG值为0,就能造成奇数个1。

这样证明了先手必胜局面有至少一个后继局面为先手必败局面。

接下来需证明每一个先手必败局面都只能转移到先手必胜局面。

先来证明第二个条件:

所有SG值不超过1,那么有奇数个1先手就必败,无论如何都只能转移到有偶数个1的情况。有奇数个1即游戏的SG值不为0。

第一个条件:

如果有至少两个子游戏的SG值超过1,那么无论如何都会转移到一个先手必胜局面(有至少一个SG值超过1,游戏的SG值不为0),因为无法造成游戏的SG值继续为0。

如果只有一个子游戏的SG值超过1,这是不可能发生的,不可能存在一个局面只有一个子游戏SG值超过1但异或和为0的,一定有至少两个子游戏SG值超过1。

证毕。

习题

【JZOJ1007,SHOI2008】小约翰的游戏。

小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有n堆石子,小约翰和他的哥哥轮流取石子,每个人取的时候,可以随意选择一堆石子,在这堆石子中取走任意多的石子,但不能一粒石子也不取,我们规定取到最后一粒石子的人算输。

小约翰相当固执,他坚持认为先取的人有很大的优势,所以他总是先取石子,而他的哥哥就聪明多了,他从来没有在游戏中犯过错误。小约翰一怒之前请你来做他的参谋。自然,你应该先写一个程序,预测一下谁将获得游戏的胜利。

每堆石子数不超过5000,一共最多500组数据,n<=50。

设sg[i]表示一堆石子含有i个石头的估价函数。显然

sg[i]=mexj<i{sg[j]}

我们算出sg函数,然后用SJ定理解决此题。

复杂度O(50002+T∗N)

但实际上,可以发现sg[i]=i。

可以省去转移的复杂度。

#include<cstdio>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
bool czy;
int i,j,k,l,t,n,m,ca;
int main(){
scanf("%d",&ca);
while (ca--,ca>=0){
scanf("%d",&n);
czy=0;
l=0;
fo(i,1,n){
scanf("%d",&t);
l^=t;
if (t>1) czy=1;
}
if (!czy){
if (n%2) printf("Brother\n");else printf("John\n");
}
else {
if (l) printf("John\n");else printf("Brother\n");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: