组合博弈基础 -- 三大基本博弈+斐波那契博弈

#define P(positive) 必胜态
#define N(negative) 必败态

巴什博奕(Bash Game)

有一堆石子n个 , 每次可以取1~m个石子 , 没有石子可取的那方输 , 问第一个取的人的输赢

给对面一个m+1的堆 , 无论对面取多少 , 你都可以取一个数使这堆石子取完

所以说你要设法取一个数使堆剩下 k ( m + 1 ) 给对面

int main(){
    LL t;sf(t);
    while(t--){
        LL n,m;sf(n),sf(m);
        if(n%(m+1))printf("first\n");//可以把k*(m+1)这个状态给对面
        else printf("second\n");//到自己手上的状态是k*(m+1)
    }
}

变形(最后取的输) :

只要留一块给对面就赢了,所以判断(n-1)%(m+1)




威佐夫博奕(Wythoff Game)

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

我们用 ( a [ k ] b [ k ] ) 表示两堆物品的数量并称其为局势,显然(0,0)为必输局势,这种必输局势我们称为奇异局势

前几个奇异局势是:(0,0),(1,2),(3,5),(4,7),(6,10),(8,13),(9,15),(11,18),(12,20)

a[k]为前k-1中的a[i]b[i]中未出现的最小非负整数 , b[k]=a[k]+k (a[0]=b[0]=0)


公式 :

a [ k ] = [ k ( 1 + 5 ) / 2 ] , b [ k ] = a [ k ] + k


性质 :

  1. 每个自然数只会出现在一个奇异局势1
  2. 奇异局势在一次操作后变成非奇异局势2
  3. 非奇异局势可以在一次操作后变成奇异局势3


奇异局势判断 :

直接判断小的那堆能不能写成上面a的公式 ( k = b a )

int main(){
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF){
         if(a>b)swap(a,b);
         int k=b-a;
         int ans=(a!=round(k*(1.0+sqrt(5.0))/2));
         printf("%d\n",ans);//0必败态 1必胜态
    }
    return 0;
}




尼姆博奕(Nimm Game)

有k堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

对于多堆石子 ( a 1 , a 2 , a 3 . . . a k ) , 如果所有都是0 , 那么是一个N态

当多堆石子异或后结果为0 , 则是一个N态 , 否则为P态4

int main(){
    int m,ans,n;
    while(~scanf("%d",&m)){
        n=ans=0;
        while(m--)
            scanf("%d",&n),ans^=n,printf("ans=%d\n",ans);
        if(ans)printf("Yes\n");
        else printf("No\n");
    }
}

证明:

当你拿到一个异或为0的状态(没取完) , 取任意一个数 , 这个数因为不能是0 , 所以在二进制中一定有几位会是1 , 也就是说剩下的异或后不会是0即还有石子没取完

而可以证明对方可以取一个数使剩下的石子数异或为0 , 即:维护异或为0的状态

证明 : 假设异或后为(1010) , 那么一定会有一堆石子的大小大于>=(1000) , 我们对于这堆取出(1000) , 剩下的就变成了(0010) , 这个时候我们再塞回去(0010) ( 这个是重点 , 塞在自己的堆里面 , 和别的堆的0010异或后为0000 , 如果0010是自己堆的就更好处理了 , 直接取掉变成0就可以了 ) , 就可以了


斐波那契博弈(Fibonacci Game)

有一堆石子,两个人轮流从其中取走一定的石子,取走最后所有石子的人为赢家,不过得遵循如下规则:
1.第一次取不能取完,至少取1颗.
2.从第二次开始,每个人取的石子数至少为1,至多为对手刚取的石子数的两倍。

当n为Fibonacci数时,先手必败。否则先手必败

证明:根据“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。如n=83 = 55+21+5+2,假如先手取2颗,那么后手无法取5颗或更多,而5是一个Fibonacci数,那么一定是先手取走这5颗石子中的最后一颗,同理,接下去先手取走接下来的后21颗中的最后一颗,再取走后55颗中的最后一颗,那么先手赢。

反之:如果n是Fibonacci数,如n=89:记先手一开始所取的石子数为y
(1)若y>=34颗(也就是89的向前两项),那么一定后手赢,因为89-34=55=34+21<2*34。
(2)y<34时剩下的石子数x介于55到89之间,它一定不是一个Fibonacci数,把x分解成Fibonacci数:x=55+f[i]+…+f[j],若,如果f[j]<=2y,那么对B就是面临x局面的先手,所以根据之前的分析,后手只要先取f[j]个即可,以后再按之前的分析就可保证必胜。






注脚 :


  1. 显然 , a是递增的 , 而k也是递增的 , 所以b是递增的 , 所以只需要排除a和b重复的情况 .
    a [ i ] , b [ j ] , 如果 i < j , 那么 b [ j ] > a [ i ] ; 反之 , a是会选择前面没有出现过的数 , 自然不会选和b相同的数
  2. 减一个数的话 , 因为所有数不重复 , 另一个数肯定不会和剪掉后的那个数匹配
    两个减去相同数 , 因为k也是不会重复的 , 所以剪完后也不会是奇异局势
  3. 对于每个数 , 一定可以找到一组奇异局势满足这个数在其中出现 , 那么如果匹配了a , 局势中的b比你手中的b小 ,你就可以得到局势中的b ; 如果不行就匹配b , 可证明a或b一定有一个可以完成匹配

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/82082653