阶梯博弈——学习笔记

来源:互联网 发布:python 一段时间日期 编辑:程序博客网 时间:2024/05/17 10:41

今天刚做了一道题。
POJ 1704

题目大意
Georgia and Bob在玩游戏(求Alice心理阴影面积),在一个1行的格子上,有N个棋子,两人轮流操作,Georgia永远先手,每次操作需要将一个棋子向左移动,至少要移动一个格子,但不可以移到左边缘,或者越过其他的格子,无法操作的人输,问对于给出的状态是否有必胜策略。

一开始还以为是针对于每个棋子最终能移动到哪里,将这之间需要的总移动数看做石子堆的数量,但这样做真的错的很离谱。因为棋子的位置对后继状态产生影响,所以不能这样做。

对于这类题目,从简单的情况入手:
N=1不解释。
N=2,如果这两个棋子靠拢在一起,那么不难推出,先手必败,因为先手只能移前面的棋子,而后手只需将后面的棋子移到前面,使两枚棋子靠拢,最后先手必败,反之,先手就会让后面的棋子移到前面靠拢,从而保证自己必胜。
N=3,应该也是靠拢的原则,就是一开始应该2向1靠拢,还是3向2靠拢?其实很难想。

还是直接给出来,这道题其实是和奇偶有关的博弈,nim游戏的进阶版,称为阶梯博弈。

阶梯博弈的模型如下:
给出N个台阶,每个台阶上有ai个石子,把地面当做台阶0,双方轮流操作,每次将一个台阶上,至少选1个,最多全取,的石子取出,并放在左边一个台阶上,没有操作策略的人输(此时所有石子都在地面上),问先手是否有必胜策略。
这里写图片描述
没错,这图网上copy来的,不过网上大多数将这个的都是互相转(chao)载(xi),错误也一并co下来了。

给出阶梯博弈先手必胜的条件:

当且仅当所有奇数上的台阶的石子数的异或和不为0时,先手必胜。

下面给出(并不严谨的)证明:

在模型中已经说明,把地面看做台阶0,那么在偶数台阶上的石子是可以自动忽略的,因为即使把后手把偶数台阶上的石子移到奇数台阶上,我们依旧可以把刚移过来的石子再原封不动往下再移一次,这么挪来挪去,这些石子就会移到地面上,然后,这些石子就没意义了,哪怕后手中途放弃这些石子去操作,如果还是去操作偶数台阶上的,前面已经有讲过,如果操作奇数上的,那我们就认真玩,因为我们已经知道所有奇数台阶上石子的异或和不为0,那就可以保证,当我们某一次操作之后,后手就会发现只有一些偶数台阶上有石子了,然后我们跟着后手的步骤,然后就磨死他了。

或者这么说。我们按照尼姆博弈的原则进行第一次移动。如果对方移动奇数号阶梯的石子,我们继续按照尼姆博弈的原则移动。如果对方移动的是偶数号阶梯的石子,及对方将偶数号阶梯的石子移动到了奇数号(对奇数号产生了影响),我们就接着将对方移动到奇数号的石子再向下移动一个台阶,移动到偶数号。这就意味着在偶数号的棋子对我们的博弈是没有影响的(这段话是copy来的,我怕自己说话说不清楚,这篇文章讲的挺好,毕竟很少有正确的关于阶梯博弈的文章,有些疑问可以看它)。

好吧,回到POJ 1704,现在就是把问题转化为模型上来。首先就是靠拢,很明显奇数棋子和偶数棋子之间的空隙大小是与石子数等价的,但是当N为奇数时,因为我们说了是奇数,所以第一个棋子可以理解为在0的位置有一个编号为0的棋子,而1棋子就向它靠拢。那么这道题就可以转化成nim博弈了。

#include<cstdio>#include<algorithm>using namespace std;int tst,n,ans,a[10005];inline void readi(int &x){    x=0; char ch=getchar();    while ('0'>ch||ch>'9') ch=getchar();    while ('0'<=ch&&ch<='9')    {        x=x*10+ch-'0';        ch=getchar();    }}int main(){    freopen("georgia.in","r",stdin);    freopen("georgia.out","w",stdout);    readi(tst);    while (tst--)    {        readi(n); ans=a[0]=0;        for (int i=1;i<=n;i++) readi(a[i]);        sort(a+1,a+n+1);        for (int i=n;i>0;i-=2) ans^=(a[i]-a[i-1]-1);        if (ans) printf("Georgia will win\n");        else printf("Bob will win\n");    }    return 0;}

更新于 2017年10月8日19:51:20

有权限的双倍经验题 BZOJ 1115 Orz Lynstery

有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数。两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件谁没有石子可移时输掉游戏。问先手是否必胜。

考虑相邻石子的差值,当取第i堆石头时,i与i-1的差值的减小量等于i+1与i的差值的增加量,且要时刻保证差值非负。 这样就转换成阶梯博弈模型了。

#include<cstdio>#include<algorithm>using namespace std;int tst,n,ans,a[10005];inline void readi(int &x){    x=0; char ch=getchar();    while ('0'>ch||ch>'9') ch=getchar();    while ('0'<=ch&&ch<='9')    {        x=x*10+ch-'0';        ch=getchar();    }}int main(){    freopen("stone.in","r",stdin);    freopen("stone.out","w",stdout);    readi(tst);    while (tst--)    {        readi(n); ans=a[0]=0;        for (int i=1;i<=n;i++) readi(a[i]);        for(int i=n;i>=1;i-=2) ans^=(a[i]-a[i-1]);        if(ans) printf("TAK\n"); else printf("NIE\n");     }    return 0;}
原创粉丝点击