常见的几种博弈综合

最经典的Nim博弈:

描述:
一共有 N N N 堆石子,编号 1 ~ n n n ,第 i 堆中有个 ai 个石子。每一次操作 Alice 和 Bob 可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。两个人轮流行动,取走最后一个的人胜利。

结论:对某个人来说都有一个局面,当且仅当 a1 ^ a2 ^ …… ^ an = 0时,必败,否则必胜。

反Nim博弈:

描述:
与经典 Nim 博弈相反,取走最后一颗石子的人失败。
结论:一个状态为必胜态,当且仅当:1、所有堆石子个数为1且 Nimsum = 0;2、至少有一堆石子个数大于1,且Nimsum ≠ 0。(Nimsum就是所有ai的异或)。

Moore’s Nimk:

描述:
两个人玩取石子游戏,共有 N N N 堆石子,每个人每次可以从 k k k 堆石子里面任意多个石子,不能取的人输。
结论:把 N N N 堆石子的石子数用二进制表示,统计每个二进制位上 1 1 1 的个数,若每一位上 1 1 1 的个数 mod( k k k +1) 全部为0,则必败,否则必胜。

威佐夫博弈:

描述:
两堆石子,每次可以取一堆或两堆,从两堆中取得时候个数必须相同,先取完的胜。

结论:设两堆石子分别有 n n n 个和 m m m 个石子 ( n > m ) (n > m) (n>m),则如果 m = [ ( n − m ) ∗ ( 1 + s q r t ( 5 ) ) / 2 ] m = [(n - m) * (1+sqrt(5)) / 2] m=[(nm)(1+sqrt(5))/2] ,先拿者输。(其中[ ]为向下取整函数)

巴什博奕:

描述:
有一堆石子共 n n n 个。每次从最少取 1 1 1 个,最多取 m m m 个,最后取光的人胜。

结论:如果 n = ( m + 1 ) ∗ k + r ( r ! = 0 ) n=(m+1) * k + r (r != 0) n=(m+1)k+r(r!=0) 那么先手一定必胜,因为第一次取走 r r r 个,接下来无论对手怎么取,我们都能保证取到所有 ( m + 1 ) (m+1) (m+1) 倍数的点,那么循环下去一定能取到最后一个。

Take-and-Break Game:

描述:
n堆石子,每次可以取走一堆石子,然后放入两堆规模更小的石子(可以为0).最后不能操作的人输。

结论:容易看出每堆石子都是单独的,所以可以用SG定理求解。然后枚举x能转移到的所有状态,算出这些后继的sg值后取mex即可。(x的一个后继为sgi^ sgj, i < x, j < x)。用记忆化搜索,复杂度O(n).

staircase nim1:

描述:
一个 n 个台阶的楼梯,每个楼梯上有 ai 个石子。操作为可以拿从一层楼梯上拿若干个石子到下一层楼梯 (不能不拿),拿到地面上的石子不能再拿。问先手胜还是后手胜。

结论:通过后手相消的原则,我们可以发现偶数层楼梯上的石子是无效的(因为如果一方从偶数层上拿了若干石子到奇数层,另一方再把它们拿到偶数层就可以了,而地面(第0层)是偶数层,所以最终这些石子是没有意义的)。所以只要对奇数堆做Nim判断即可知道胜负情况。

staircase nim2:

描述:
一个n个台阶的楼梯,每个台阶上有ai个石子。操作为可以把一个石子拿到下面任何一个台阶 (或地面),地面上的石子不能再拿。问先手胜还是后手胜。
结论:把每个石子看做nim游戏中的一堆即可。

斐波那契博弈:

描述:
有一堆个数为n的石子,游戏双方轮流取石子,满足:
1)先手不能在第一次把所有的石子取完;
2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。
约定取走最后一个石子的人为赢家。

结论:如果先手是斐波那契数,那么先手输,否则先手胜。

K倍动态减法游戏:

这是斐波那契博弈的扩展,把第二步的两倍变成了k倍。

  1. 当 k = 1 时,必败态为 n = 2i, 因为我们把数按二进制分解后,拿掉二进制的最后一个1,那么对方必然不能拿走倒数第二位的1,因为他不能拿的比你多。你只要按照这个策略对方一直都不可能拿完。所以你就会赢。而当分解的二进制中只有一个1时,因为第一次先手不能全部取完,所以后手一定有办法取到最后一个1,所以必败!

  2. 当 k = 2 时,赤裸裸的Fibonacci博弈了。

  3. 当 k 取任意非零正值时,重点来了:
    犹如Fibonacci博弈,我们首先要求一个数列,将n分解成数列中一些项的和,然后就可以按Fibonacci博弈的解决方法来完成,也可以按二进制的方法来理解,每次取掉最后一个1 还是符合上面的条件。

    我们用a数组表示要被求的数列,b数组中的b[i]保存 a[0…i] 组合能够构造的最大数字。这儿有点难理解,所谓构造就是指n分解为Fib数相加的逆过程。举例说明,当k = 2 时,a[N]={1, 2, 3, 5, 8, 13, 21, 33…} (Fibonacci数组);那么b[3] 即 1、2、 3 能够构造的最大数字,答案是4,有点匪夷所思?或许你会问为什么不是5、6或者其它的什么,其实是这样的 ,4 能分解成 1+3 是没有争议的,但5能分解成2+3吗? 不能,因为5本身也是Fibonacci数;6虽然能分解,但不是分解成1+2+3,而是分解成1+5。

    经过上述,我们知道b[i] 是 a[0…i] 能够构造出的最大数字,那么a[i +1] = b[i]+1;因为a数组(Fib数组)所存的数字都是不可构造的(取到它本身就是必败态),显然a[0…i]构造的最大数字 + 1 即为下一个不可构造的数字了(a[i + 1])。

    然后关于b[i]的计算,既然是a[0…i]构造最大数字,那么 a[i]是一定要选用的(这儿需要一定的推理,a[i]构造数字时,相邻的j个是不能同时用的,就像上述的2、3不能构造出5一样,推理请自己完成),那么要选用的下一项只能递减寻找,直到找到 a[t] 满足 a[t] * K < a[i] ,而b[t]就是a[0…t]所能构造的最大数字,再加上a[i], 即为a[0…i]能构造的最大数字,于是b[i] = b[t] + a[i]。

    求的数列后,之后的工作就简单了,跟Fibonacci博弈一样一样的,如果n=数列中的数,则必败,否则必胜;必胜时还要求输出第一步取法,其实就是分解的数列中最小的一个。

        a[0] = b[0] = 1;//构建数列
        int i = 0, j = 0;
        while(n > a[i])
        {
            i++;
            a[i] = b[i-1]+1;
            while(a[j+1]*k  < a[i])
                j++;
            if(k*a[j] < a[i])
                b[i] = b[j] + a[i];
            else
                b[i] = a[i];
        }

乘法博弈:

描述:
给定一个整数n,初始值p = 1,两个人A,B依次给p乘上一个区间 [a, b] 上的一个整数,当一个人乘完之后的 p 大于等于 n 时获胜。

结论:后手必败:只要给出的n在[bn * a n-1+1, bn * a n] 内,后手必败。或者找到最开始的一组区间(两个),第一个区间先手必胜,第二个区间后手必胜,然后n % a*b,看落在哪个区间里就行了。

SG函数 & SG定理:

首先定义一个游戏规则:
1、A,B,2人做游戏。
2、A,B 交替进行某种游戏规定的操作,每操作一次,选手可以在有限的操作(操作必须合法)集合中任选一种。
3、对于游戏的任何一种可能的局面,合法的操作集合只取决于这个局面本身,不取决于其它因素 (跟选手,以前的所有操作无关),如果当前选手无法进行合法的操作,则为负。

然后定义一些名词:
必败点(P点):前一个(previous player)选手将取胜的点称为必败点。
必胜点(N点):下一个(next player)选手将取胜的点称为必胜点。
性质:
1、所有的终结点都是必败点。
2、从任何必胜点操作,至少有一种方式进入必败点。
3、无论如何操作, 从必败点都只能进入必胜点。

SG函数:

先定义 mex (minimal excludant) 运算,这是施加于一个集合的运算,表最小的不属于这个集合的非负整数。例如 mex {0, 1, 2, 4} = 3 、mex {2, 3, 5} = 0、mex { } = 0。 对于任意状态 x,定义 SG(x) = mex(S), 其中 S 是 x 后继状态的 SG 函数值的集合。如 x 有三个后继状态分别为 SG (a), SG (b), SG ( c ) , 那么SG(x) = mex { SG(a), SG(b), SG© }。这样 集合 S 的终态必然是空集,所以SG函数的终态为 SG(x) = 0,当且仅当 x 为必败点P时。

计算1~n的SG函数值步骤如下:
1、使用 数组 f 将 可改变当前状态 的方式记录下来。
2、然后我们使用另一个数组将当前状态 x 的后继状态标记。
3、最后模拟 mex 运算,也就是我们在标记值中搜索未被标记值 的最小值,将其赋值给SG(x)。
4、我们不断的重复 2 - 3 的步骤,就完成了计算 1~n 的函数值。

模板:

//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//vis[]:为x后继状态的集合
int f[N],SG[MAXN];
bool vis[MAXN];
void  getSG(int n)
{
    int i,j;
    memset(SG,0,sizeof(SG));//一般在主函数里初始化一次就可以
    for(i = 1; i <= n; i++) //因为SG[0]始终等于0,所以i从1开始
    {
        memset(vis,0,sizeof(vis));//每一次都要将上一状态的后继集合重置
        for(j = 0; f[j] <= i && j <= N; j++)
            vis[SG[i-f[j]]] = 1;  //将后继状态的SG函数值进行标记,即 mex 中的SG值全部标记
        for(j = 0;; j++)
            if(!vis[j])    //查询当前后继状态SG值中最小的非零值
                break;
        SG[i] = j;
    }
}
SG定理:

游戏和的SG函数等于各个游戏SG函数的Nim和(即Nimsum)。这样就可以将每一个子游戏分而治之,从而简化了问题。
即:sg(X) = sg(x[1]) ^ sg(x[2]) ^ … ^ sg(x[n])。

翻硬币游戏:

(以下情况证明见 https://www.cnblogs.com/kuangbin/p/3218060.html
一般的翻硬币游戏的规则是这种:N 枚硬币排成一排。有的正面朝上。有的反面朝上。我们从左開始对硬币按1 到N 编号。
第一,游戏者依据某些约束翻硬币,但他所翻动的硬币中,最右边那个硬币的必须是从正面翻到反面。
第二,谁不能翻谁输。

约束条件一:每次仅仅能翻一个硬币。

有奇数个正面硬币。局面的SG值 = 1,先手必胜,有偶数个正面硬币,局面的SG值 = 0。先手必败。

约束条件二:每次能翻转一个或两个硬币。(不用连续)

每一个硬币的SG值为它的编号。初始编号为0。与NIM游戏是一样的。

约束条件三:每次必须连续翻转k个硬币。

sg的形式为000…01 000…01,当中一小段0的个数为k-1。

约束条件4:每次翻动一个硬币后。必须翻动其左側近期三个硬币中的一个,即翻动第x个硬币后。必须选择x-1。x-2,x-3中的当中一个硬币进行翻动,除非x是小于等于3的。(Subtraction Games)

这个与每次最多仅仅能取3个石子的取石子游戏的SG分布一样,相同还有相似的这类游戏,约束条件5也是一样。

约束条件5:每次必须翻动两个硬币,并且这两个硬币的距离要在可行集S={1,2,3}中。硬币序号从0開始。(Twins游戏)

sg[3k] = 0, sg[3k+1] = 1, sg[3k+2] = 2, sg[3k+3] = 3。

约束条件6:每次能够翻动一个、二个或三个硬币。(Mock Turtles游戏)

我们称一个非负整数为odious,当且仅当该数的二进制形式的1出现的次数是奇数,否则称作evil。所以当2x为odious时,sg[x]的值是2x,当2x是evil时。sg[x]的值是2x+1。

约束条件7:每次能够连续翻动随意个硬币,至少翻一个。(Ruler游戏)

SG函数是g(n)=mex{0,g(n-1),g(n-1)g(n-2),…,g(n-1)…^g(1)}。sg值为x的因数其中2的能达到的最大次幂。比方14=27,最大1次幂,即2。16=2222,最大4次幂,即16。

约束条件8:每次必须翻转4个对称的硬币,最左与最右的硬币都必须是从正翻到反。(開始的时候两端都是正面)(Grunt游戏)

这是Grundy游戏的变种,初始编号从0開始。当首正硬币位置为0,1,2时是terminal局面,即 终结局面,sg值都是0。当首正硬币位置n大于等于3的时候的局面能够通过翻0, x, n-x, n四个位置得到(当中x<n/2可保证胜利)。这就像是把一堆石子分成两堆不同大小石子的游戏,也就是Grundy游戏。

树上删边游戏定理:

(具体见 https://blog.csdn.net/wu_tongtong/article/details/79311284

克朗原理:

对于树上的某一个点,ta的分支可以转化成以这个点为根的一根竹子,这个竹子的长度就是它各个分支的边的数量的异或和。

费森原理:

环上的点可以融合,且不改变图的SG值。一般来说 我们可以把一个带有奇数边的环等价成只有一个端点的一条边 而偶数边的环等价于一个点。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值