ACM常见组合博弈游戏

这两天认识了几个组合游戏的基础模型,希望自己能更新下去。。

Ferguson游戏

Description

  • Initial

有两个盒子,一个装有 m 颗糖,一个装有 n 颗糖,表示为 (m, n) .

  • Step

每次清空一个盒子,将另一个盒子里的糖转移一些过来,并保证两个盒子至少各有一颗糖。

  • Win

最后进行转移糖者胜,无法转移糖者败。

Solve

m, n 都为奇数,先手败;m, n 至少一个为偶数,先手胜。

Proof

显然,初始状态为(1, 1),先手必败;

  • 设 max(m, n) = 2,即初始状态为 (1, 2),(2, 1) 或 (2, 2),对于 (1, 2),(2, 1) 先手可以把 1 清空,然后将 2 分为 (1, 1) ,先手胜;对于 (2, 2) ,先手可以把其中一个 2 清空,然后将另一个 2 分为 (1, 1) ,先手胜。符合结论。

  • 设 max(m, n) < k 均符合结论,当 max(m, n) = k :

    • 设 m 与 n 至少一个为偶数(假设m是偶数),则将 n 清空,把 m 分为两个奇数 (a, b) ,由于max(a, b) < k ,因此(a, b) 必败,(m, n) 必胜(利用规则2);

    • 设 m 与 n 均为奇数,则只能把其中一个数分为一个奇数 a ,一个偶数 b ,由于max(a, b) < k ,因此对于任何的方式分解出的(a, b) 均必胜,(m, n) 必败(利用规则1);

    • 故 max(m, n) = k 符合结论。

  • 故对于任意 (m, n) 结论成立。

chomp!游戏

Description

  • Initial

有一个 m * n 的棋盘,棋盘的每一个格子用(x, y)表示,最左下角是(1, 1),最右上角是(m, n) ;

  • Step

每次可以拿走一个方格,并拿走该方格右边与上边的所有方格。

  • Win

谁拿到(1, 1)谁败。

Solve

当 m = n = 1,先手败;除此之外,先手均有必胜策略(先手胜)。

Proof

反证法:

假设后手能取得胜利,那么先手可以第一步拿走(m, n),若后续回合内后手通过拿走(x, y)达到了必胜状态,先手均可以第一步就拿走(x, y)来达到必胜状态。
故不存在后手必胜状态。

由于无法给出构造性证明,所以只能证明先手必胜,而不能给出广义的必胜策略。

约数游戏

Description

  • Initial

桌上有 n 个数字:1~n。

  • Step

两人轮流在选择一个桌上的数 x ,然后将 x 与 x 的约数都拿走。

  • Win

拿去最后一个数的人胜出(无法选择数字的人失败)。

Solve

先手有必胜策略。(先手胜)

Proof

这个游戏是 chomp! 的思想的应用。

假设后手能取得胜利,那么先手可以第一步拿走 1,若后续回合内后手通过拿走 x 达到了必胜状态,先手均可以第一步就拿走 x 来达到必胜状态。

Bash Game(巴什博弈)

Description

  • Initial

n 个物品堆成一堆。

  • Step

两个人轮流从这堆物品中取物,规定每次至少取一个,最多取 m 个。

  • Win

最后取光者得胜。(无法取者败)

Solve

如果 n % (m+1)0 ,则先手必胜。

Proof

  • 如果 n=m+1 , 显然,先手无论取多少,后手均可以将剩余物品一次全取走,所以先手败。

  • 如果 n=k(m+1) ,我们从后手的角度来考虑,设先手第一次取走 x 个物品,那么后手只要再取走 m+1x 个,此时剩余物品数量变为 (k1)(m+1) 个,一直重复这个步骤,就可以回到先手面临 n=m+1 的局面,所以还是先手败。相当于进行了k次 n=m+1 的游戏。

  • 如果 n=k(m+1)+s ,先手一开始取走 s 个物品,那么后手就会面临 n=k(m+1) 的局面,所以先手胜。

  • 结论得证。

例题

  1. 有一个游戏,在一个n*m的矩阵中起始位置是(1, m),走到终止位置(n, 1);游戏规则是只能向左,下,左下方向移动一步,先走到终点的为获胜者。(HDU 2147,总共有 n + m 的距离要移动,一次最多移动 2 的距离,故判断 (n + m)%2 是否为 0 即可)。

  2. bash博弈变形1——减法博弈:
    两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。(先手必胜,第一次报1,类似:HDU 2149)
    有一个由n个石子组成的石子堆,两名玩家轮流从中拿走石子,每次拿走石子的个数只能是集合S中的数。拿走最后一枚石子的玩家获胜。(状态转移即可)

  3. bash博弈变形2:初始状态下有石子n个,除最后一次外其他每次取物品个数必须在[p,q]之间,最后一次取硬币的人输。(HDU 2897)
    这题状态稍微复杂一些,并且胜负条件与之前相反,一般bash博弈里每次取个数可以看作在[1,m]之间,胜负手判断为 n % (1+m) ,因此我们可以猜想每次取[p,q]的胜负手判断为 n % (p+q) ,通过验证猜想我们可以发现如下策略:

    • n=k(p+q) 时,先手第一次取 q 个,随后的回合若后手取 x 个,先手再取 p+qx 个,那么最后就会留给后手 p 个,先手胜。
    • n=k(p+q)+s 时,则要分情况考虑:
      1. 若 s 在 [1,p] 之间,先手取 x,后手可以取 p+qx ,最后留给先手 s 个,后手胜;
      2. 若 s 在 (p,p+q) 之间,先手任取 x 个 (1sx<p) ,后手取 y 个,先手可以再取 p+qy ,最后留给后手 s - x 个,先手胜;
//HDU 2897
#include
using namespace std;

int main()
{
    int n , p , q ;
    while ( cin >> n >> p >> q ) {
        if ( n % ( p + q ) <= p && n % ( p + q ) ) 
            puts( "LOST" ) ;
        else
            puts( "WIN" ) ;

    }
    return 0;
}

Wythoff’s Game(威佐夫博弈)

Description

  • Initial

有两堆石子,一堆有 m 个,另一堆有 n 个。

  • Step

双方轮流取走一些石子,合法的取法有如下两种:
1. 在一堆石子中取走任意多颗;
2. 在两堆石子中取走相同多的任意颗.

  • Win

取走最后一颗石子的人为赢家。

Solve

(1,2) (1,2) (2,1) (2,1) 视为同一状态,
第 k 个必败状态是 (\lfloor\frac{\sqrt 5 +1}{2}*k \rfloor+k,\lfloor\frac{\sqrt 5 + 1}{2}*k \rfloor) (5+12k+k,5+12k)

  • 拓展性质:

    • (m(k),n(k))=(\lfloor\frac{\sqrt 5 + 1}{2}*k \rfloor+k,\lfloor\frac{\sqrt 5 + 1}{2}*k \rfloor) (m(k),n(k))=(5+12k+k,5+12k) ,则 m(k) 也表示前 k 个必败状态中没出现的自然数。

    • 每个自然数都会出现在必败状态中且仅会出现一次。

//HDU 1527 模板题
#include
using namespace std;

int main()
{
    int a , b ;
    while ( cin >> a >> b ) {
        if ( a < b ) swap( a, b ) ;
        int k = a - b ;
        int n = (int)( k * ( sqrt(5.0) + 1.0 ) / 2 ) ;
        bool win = ( n != b ) ;
        cout << win << endl ;
    }
    return 0;
}

Proof

还是打表吧。。

const int N = 50 ;
bool win[100][100] ;
void init() 
{
    memset( win , false , sizeof win ) ;
    for ( int i = 1 ; i <= n ; i++ ) win[i][i] = true ;
    for ( int i = 1 ; i <= n ; i++ ) {
        for ( int j = i+1 ; j <= n ; j++ ) {
            if ( !win[i][j] ) {
                for ( int k = j + 1 ; k <= n ; k++ )
                    win[i][k] = win[k][i] = true ;
                for ( int k = i + 1 ; k <= n ; k++ )
                    win[k][j] = win[j][k] = true ;
                for ( int k = 1 ; k <= n ; k++ )
                    win[i+k][j+k] = win[j+k][i+k] = true ;
            }
        }
    }
    for ( int i = 1 ; i <= n ; i++ )
        for ( int j = i ; j <= n ; j++ ) {
            if ( !win[i][j] ) cout << i << ' ' << j << endl ;
        }
}

Fibonacci’s Game (斐波那契博弈)

Description

  • Initial

有一堆个数为 n >= 2 的石子。

  • Step

双方轮流取石子,满足以下条件:
1. 先手不能在第一次把所有的石子取完;
2. 之后每次可以取的石子数介于 1 到对手刚取的石子数的 2 倍之间(包含 1 和对手刚取的石子数的 2 倍)。

  • Win

取走最后一个石子的人为赢家。

Solve

如果 n 是斐波那契数,则后手胜;反之,先手胜。

\\HDU 2516 模板题
#include
using namespace std;

const string win[2] = { "Second win" , "First win" } ;
long long fib[100] ;
int init() 
{
    fib[0] = 1 ; fib[1] = 1 ;
    for ( int i = 2 ; i < 100 ; i++ ) {
        fib[i] = fib[i-1] + fib[i-2] ;
        if ( fib[i] > ( 1LL << 35 ) ) return i ;
    }
    return 100 ;
}
int main()
{
    int n ; int len = init() ;
    while ( cin >> n && n ) {
        int ok = 1 ;
        for ( int i = 0 ; i <= len ; i++ ) {
            if ( fib[i] == n ) {
                ok = 0 ;
                break ;
            }
        }
        cout << win[ok] << endl ;
    }
    return 0;
}

Proof

Zeckendorf定理:任何正整数可以表示为若干个不连续的 Fibonacci 数之和。

先手必须从 <= n / 3 的数量开始取(例如第一次取的数量 > n/3,那么后手可以直接取完所有剩下石子)
于是。。。太绕了!
还是请看Acdream大神的吧。。http://blog.csdn.net/acdreamers/article/details/8586135

Nim游戏(待施工)

Description

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

Solve

介绍这个游戏的太多了。。偷个懒
具体解法就是异或异或!

你可能感兴趣的:(数学-数论,ACM,and,novicer)