ACM常见组合博弈游戏
2015-11-07 00:03
253 查看
这两天认识了几个组合游戏的基础模型,希望自己能更新下去。。
有两个盒子,一个装有 m 颗糖,一个装有 n 颗糖,表示为 (m, n) .
Step
每次清空一个盒子,将另一个盒子里的糖转移一些过来,并保证两个盒子至少各有一颗糖。
Win
最后进行转移糖者胜,无法转移糖者败。
设 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) 结论成立。
有一个 m * n 的棋盘,棋盘的每一个格子用(x, y)表示,最左下角是(1, 1),最右上角是(m, n) ;
Step
每次可以拿走一个方格,并拿走该方格右边与上边的所有方格。
Win
谁拿到(1, 1)谁败。
假设后手能取得胜利,那么先手可以第一步拿走(m, n),若后续回合内后手通过拿走(x, y)达到了必胜状态,先手均可以第一步就拿走(x, y)来达到必胜状态。
故不存在后手必胜状态。
由于无法给出构造性证明,所以只能证明先手必胜,而不能给出广义的必胜策略。
桌上有 n 个数字:1~n。
Step
两人轮流在选择一个桌上的数 x ,然后将 x 与 x 的约数都拿走。
Win
拿去最后一个数的人胜出(无法选择数字的人失败)。
假设后手能取得胜利,那么先手可以第一步拿走 1,若后续回合内后手通过拿走 x 达到了必胜状态,先手均可以第一步就拿走 x 来达到必胜状态。
n 个物品堆成一堆。
Step
两个人轮流从这堆物品中取物,规定每次至少取一个,最多取 m 个。
Win
最后取光者得胜。(无法取者败)
如果 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) 的局面,所以先手胜。
结论得证。
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 个,先手胜;
有两堆石子,一堆有 m 个,另一堆有 n 个。
Step
双方轮流取走一些石子,合法的取法有如下两种:
1. 在一堆石子中取走任意多颗;
2. 在两堆石子中取走相同多的任意颗.
Win
取走最后一颗石子的人为赢家。
第 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 个必败状态中没出现的自然数。
每个自然数都会出现在必败状态中且仅会出现一次。
有一堆个数为 n >= 2 的石子。
Step
双方轮流取石子,满足以下条件:
1. 先手不能在第一次把所有的石子取完;
2. 之后每次可以取的石子数介于 1 到对手刚取的石子数的 2 倍之间(包含 1 和对手刚取的石子数的 2 倍)。
Win
取走最后一个石子的人为赢家。
先手必须从 <= n / 3 的数量开始取(例如第一次取的数量 > n/3,那么后手可以直接取完所有剩下石子)
于是。。。太绕了!
还是请看Acdream大神的吧。。http://blog.csdn.net/acdreamers/article/details/8586135
物品,规定每次至少取一个,多者不限,最后取光者得胜。
具体解法就是异或异或!
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
Initialn 个物品堆成一堆。
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
介绍这个游戏的太多了。。偷个懒具体解法就是异或异或!
相关文章推荐
- 我是运营,我没有假期
- 每个 Linux 游戏玩家都绝不想要的恼人体验
- 在 Fedora 上使用 Steam play 和 Proton 来玩 Windows 游戏
- Steam 让我们在 Linux 上玩 Windows 的游戏更加容易
- 如何使用 Steam Play 在 Linux 上玩仅限 Windows 的游戏
- 新一代iPad适配应用之游戏篇
- VB实现的《QQ美女找茬游戏》作弊器实例
- C#实现洗牌游戏实例
- C#实现的算24点游戏算法实例分析
- C#实现简单的井字游戏实例
- C++编写简单的打靶游戏
- C++实现基于控制台界面的吃豆子游戏
- 纯javascript实现的小游戏《Flappy Pig》实例
- JavaScript实现俄罗斯方块游戏过程分析及源码分享
- JS小游戏之仙剑翻牌源码详解
- JS小游戏之宇宙战机源码详解
- Android基本游戏循环实例分析
- JavaScript游戏之优化篇
- js实现俄罗斯方块小游戏分享
- 用javascript做一个webgame连连看大家看下