您的位置:首页 > 其它

ACM常见组合博弈游戏

2015-11-07 00:03 253 查看
这两天认识了几个组合游戏的基础模型,希望自己能更新下去。。

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+1−x 个,此时剩余物品数量变为 (k−1)(m+1) 个,一直重复这个步骤,就可以回到先手面临 n=m+1 的局面,所以还是先手败。相当于进行了k次 n=m+1 的游戏。

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

结论得证。

例题

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

bash博弈变形1——减法博弈:

两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。(先手必胜,第一次报1,类似:HDU 2149)

有一个由n个石子组成的石子堆,两名玩家轮流从中拿走石子,每次拿走石子的个数只能是集合S中的数。拿走最后一枚石子的玩家获胜。(状态转移即可)

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+q−x 个,那么最后就会留给后手 p 个,先手胜。

n=k∗(p+q)+s 时,则要分情况考虑:

若 s 在[1,p]之间,先手取 x,后手可以取 p+q−x ,最后留给先手 s 个,后手胜;

若 s 在(p,p+q)之间,先手任取 x 个 (1≤s−x<p),后手取 y 个,先手可以再取 p+q−y ,最后留给后手 s - x 个,先手胜;

//HDU 2897
#include<bits/stdc++.h>
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√+12∗k⌋+k,⌊5√+12∗k⌋)。

拓展性质:

令(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√+12∗k⌋+k,⌊5√+12∗k⌋),则 m(k) 也表示前 k 个必败状态中没出现的自然数。

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

//HDU 1527 模板题
#include<bits/stdc++.h>
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<bits/stdc++.h>
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 博弈论