题解 P1247 【取火柴游戏】

· · 题解

更好的阅读体验请点击

忽然发现博弈论是个很好玩的东西哎

之前假期学长讲课的时候就发现这种必胜的战略可以用来坑人做题

这两天终于做了第一道博弈论的题,写篇博客纪念一下

灵感来源:洛谷P1247

Pre-scene

众所周知,李明和Jenny都喜欢Danny,为了争夺Danny的所有权,他们决定玩一个游戏。规则是这样的:

有k堆扑克牌(不成套),两人轮流从某一堆中拿出x张牌,不能不拿,也不能跨堆拿牌,拿走最后一张(堆)牌的一方获胜。

命运当然是不公平的,李明来向你请教有没有必胜的方法,你聪明的,能告诉他么

0X10 知识

先来了解一下,什么是Nim博弈

通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。               ——百度百科

了解Nim游戏之后,我们会发现这是一个经典的ICG游戏(不要问我什么是ICG因为我百度百科也不知道)

显然游戏有两种局面,先手必败和先手必胜,我们分别定义他们为P-position和N-position两种,其中P代表Previous,N代表Next,那么我们可以得出,从N-position转移而来的一定是P-position,反之也成立。

0X20 简单的证明

我们先来手玩一组小数据,一共有2堆石子,每一堆都有3个,你是先手的话,会怎么拿?由规则我们知道,玩家的目标是拿走最后一个石子,所以先手的你一定不会把某一堆全部拿走,因为如果这样,后手将会拿走另一堆,先手必败;但是如果你先拿1个,后手会跟着你从另一堆也拿一个,你拿2个,后手仍可以做和你一样的操作,先手必败。至此我们发现,(3,3)是一个P-position的局面。

推论:

1.当a1^a2^a3^……a^n=0时,为P-position,即先手必败。

2.当a1^a2^a3^……a^n!=0时,一定存在某个移动,使得局面变成一个P-position,即此局面为N-position。

0X30 实现

直接异或运算即可,代码过于简单就不放了。

0X40 题解

传送门

了解了上述知识后,这道题几乎就是一个裸的板子了,就是加了一个输出怎么取和取完的状态而已,这也很好实现。

0X41 怎么取

由于我们发现异或和不等于0,我们就要考虑怎么取使得其等于0了。

设k=a1^a2^a3^……a^n,因为k!=0,而显然k^k=0,所以要使a1^a2^a3^……a(n-1)^an^k=0,只需要把其中一个ai变成ai^k即可,又因必须要取,可得ai^k<ai

0X42 取完的状态

直接更改即可

0X43 代码

#include <cstdio>
int n;
int a[500005];
int main(){
    scanf("%d",&n);
    int check=0;
    for(int i=1; i<=n; i++){
        scanf("%d",&a[i]);
        check^=a[i];
    }
    if(!check){
        printf("lose\n");
        return 0;
    }
    for(int i=1; i<=n; i++){
        if((check^a[i])<a[i]){
            printf("%d %d\n",a[i]-(check^a[i]),i);
            for(int j=1; j<=n; j++)
                if(j!=i)
                    printf("%d ",a[j]);
                else    printf("%d ",check^a[i]);
            break;
        }
    }
    return 0;
}