avatar
fireworks99
keep hungry keep foolish

博弈

巴什博弈(Bash Game)

一堆n个物品,两人轮流取出(1~m)个,最终取完者胜出

分析

若n = m + 1,先手必败——结论①

->若n = k (m + 1),第二个人有可以使物品最终变为(m + 1)的办法,先手必败*——结论②

->若n = k (m + 1) + r,先手拿走r个,由结论②得后手必败、先手必胜*——结论③

Code

bool Bash_Game(int n, int m)
{
    if(n % (m + 1) != 0)
        return 1;
    return 0;
}

若有n堆,每堆a[i]个物品,两人来取,如何判断?

先考虑取的最大数目无上限即可以把一堆全部取完的情形:

  1. 一堆,无论数目多少,先手必胜

  2. 两堆,从最简单的情况开始考虑:

    1 、1时,后手必胜

    1、n时,先手取走(n - 1),变成上述情况,先手必胜

    a 、b时(a >= 2 && b >= 2)

    若先手取走其中一堆会输

    若先手取完变成1、n也会输(最终谁取完后局面为1、n谁就输了

    所以要保证先手取完还是c、b或a、d

  3. N堆,也是:最终谁取完后局面变为1、n谁就输了

尼姆博弈(Nimm Game)

如上述n堆物品,每堆a[i]个,两人轮流取(从其中一堆中取,数目不限),是否有先手必胜策略?

分析

如果把n堆抽象为n个非负整数,再将n个整数转化为二进制,然后对n个二进制数按位相加(不进位),若每一位相加都为偶数, 称这个状态为偶状态,否则称它为奇状态. (可以看到偶状态限制条件严苛)

可以证明:任何一个偶状态在其中一个数变小后一定成为奇状态,而一个奇状态一定可以通过改变一个数变成偶状态.

证明:

  1. 前一点很显然,因为一个数变小至少有一位发生改变,这一位就改变了原来的偶状态.
  2. 对于后一点,对于一个从高位到低位某一位和为奇的奇状态,必定有一个数的二进制表示在此位为1,对于后面的较低位和为奇的情况,只要把这个数对应位取反即可得到一个偶状态

对于n堆物品,只要判断它出是奇状态就可以断定先手有必赢策略

位运算(异或)代替求二进制判断奇状态:

如果有奇数个二进制数在第K位为1,那么在这一位上的和为奇(已可以表明这就是一个奇状态)

在位运算方面体现为:所有数字异或后得到的ans在第K位为1

这一条件可以放大为ans != 0,意味着ans至少有一位1,我们说那就是第K位。

Code

bool Nimm_Game(int n)
{
    int flag = 0;
    for(int i = 0; i < n; ++i)
        flag ^= a[i];
    if(flag)
        return 1;
    return 0;
}

但是有时会遇到n很大,a[i]为连续整数的特殊情况,这时有特殊算法

Strange code

//读入n,表示有从物品数分别1到n的n堆物品,假设n个数存在数组f[]中
int xor_n(int n)//从1到n的异或和
{
     int t = n & 3;
     if (t & 1) 
         return t / 2 ^ 1;
     return t / 2 ^ n;
}
int Nimm_Game(int n)//有必胜策略返回1
{
    int flag = 0;
    for(int i = 1; i <= n; i++)
        flag ^= xor_n(f[i]);
    if(flag) 
        return 1;
    return 0;
}

另一种解释

用(A,B,C)来表示某一特定局势,同时规定A<=B<=C。奇异局势表示先手必败。

  1. 显然(0,0,0)是奇异局势。
  2. (0,n,n)是奇异局势,当先手拿走s个石子时,我们对应拿走s个石子,最终转化为(0,0,0)
  3. (1,2,3)也是奇异局势,无论先手如何取,我们都可以转化为(0,n,n)的局势。

对于一个奇异局势(A,B,C),我们可以发现,A(XOR)B(XOR)C = 0。

下面是一条需要的前置技能:

设有数字a,b,a(XOR)b(XOR)(a(XOR)b) = (a(XOR)a)(XOR)(b(XOR)b) = 0

对于一个非奇异局势(A,B,C),我们只需要将C转化为(A(XOR)B)即可,而将C转化为(A(XOR)B)的操作为,拿走K = C-(A(XOR)B)个即可。

3可扩展到N

原文出处 : https://blog.csdn.net/pengwill97/article/details/76796070

威佐夫博弈(Wythoff Game)

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

如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:

(0,0)、(1,2)、(3,5)、(4,7)、(6,10).

可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk=ak+k.

面对奇异局势,先手必败

面对非奇异局势,必然存在使其变为奇异局势的方案,使后手必败,从而先手获胜

取石子游戏 : http://poj.org/problem?id=1067

Code

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;


int main()
{
    int a, b, k, tem;
    while(cin >> a >> b)
    {
        if(a > b)
            a ^= b, b ^= a, a ^= b;
        k = b - a;
        tem = (int)((sqrt(5.0) + 1) / 2 * k);
        if(tem == a)
            cout << '0' << '\n';
        else
            cout << '1' << '\n';

     /* double tem = (sqrt(5.0) - 1.0) / 2.0;
        k = a * tem;
        if(a != k * (int)(tem + 1))
            ++k;
        if(a + k == b)
            cout << '0' << '\n';
        else
            cout << '1' << '\n'; */
    }
    return 0;
}

文章出处: https://www.cnblogs.com/ECJTUACM-873284962/p/6398385.html

Site by Baole Zhao | Powered by Hexo | theme PreciousJoy