关闭

用SJ定理解决Anti-SG游戏

1388人阅读 评论(0) 收藏 举报
分类:

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位之后不变,那么相当于减少了一个2k1,前k-1位不论如何变,最多增加2k11。根据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+TN)
但实际上,可以发现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");
        }
    }
}
1
0