目录
1、尼姆游戏 Nim Game
给定 N 堆物品,第 i 堆物品有 Ai个。两名玩家轮流行动,每次可以任选一堆,取走任意多个物品,可把一堆取光,但不能不取。取走最后一件物品者获胜。两人都采取最优策略,问先手是否必胜。
理解:
如果n堆物品的数量异或等于0,说明起码两两配对。如果是两两配对的情况,那么后手可以跟着先手的步骤走。
比如 先手拿一堆,那后手就拿数量相同的那一堆;
如果先手拿某堆的其中1个,那后手就跟着拿与之配对的那一堆的1个;
其他的情况类型,这样可推得最后一个肯定是由后手拿的,所以,如果异或等0,先手必败!
#include <cstdio>
using namespace std;
int n, x, ans;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) {
scanf("%d", &x);
ans ^= x;
}
if(ans == 0) puts("No");
else puts("Yes");
return 0;
}
2、巴什博弈 Bash Game
有 1 堆石子,总个数是 n ,两名玩家轮流在石子堆中拿石子,每次至少取 1 个,至多取 m 个。取走最后一个石子的玩家为胜者。判定先手和后手谁胜。
结论:
如果 n%(m+1) ==0 ,先手必败
证明:
情况1:当 n ≤ m 时,显然先手获胜 此时 n%(m+1) != 0
情况二: 当 n = m + 1 时,先手最多可取走 m 个,无论其取走多少个,剩下的后手总能一次取完。
情况三: 若 ( m + 1 ) ∣ n ,假设先手拿走了 x 个,那么后手一定可以拿走 ( m + 1 ) − x 个,这样无论怎么拿剩下的石头个数都将是 m + 1 的倍数。那么最后一次取的时候石头个数必定还剩下 m + 1 个,即情况二。
int main() {
scanf("%d%d", &n, &m);
if (n % (m + 1))puts("first win");
else puts("second win");
return 0;
}
3、威佐夫博弈 Wythoff Game
有两堆石子,石子数可以不同。两人轮流取石子,每次可以在一堆中取,或者从两堆中取走相同个数的石子,数量不限,取走最后一个石头的人获胜。判定先手是否必胜。
黄金分割数
(1.0+sqrt(5.0))/2.0 = 1.618
int a,b;scanf("%d %d",&a,&b);
if(a>b)swap(a,b);
int ans = (b-a)*((1.0+sqrt(5))/2.0);
if(ans==a)puts("0\n"); // 先手必败
else puts("1\n");
4、斐波那契博弈 Fibonacci Game
有一堆个数为n的石头,双方轮流取石头。规则如下:
-
先手不能第一次就全部取完,且至少取1颗。
-
之后每次最少取1颗,至多为对手刚取石头数的2倍。
结论:
石头数为斐波那契数,则先手必败
5、博弈问题练习:
取球问题
今盒子里有n个小球,A、B两人轮流从盒中取球,两人都很聪明,不会做出错误的判断。我们约定:每个人从盒子中取出的球的数目必须是:1,3,7或者8个;轮到某一方取球时不能弃权;A先取球,然后双方交替取球,直到取完;被迫拿到最后一个球的一方为负方(输方)。
分析:像这种只能取指定个数的问题,我们可以根据先前的答案推后面的答案.
注意:a[i] 数组是指先手取 i 个球的输赢情况,不是指A或者B
a[i] = ! ( a[i-1] && a[i-3] && a[i-7] && a[i-8] )
为什么要取个非,而且各项用&呢? 思考一下,先手是我们,我们可以让后手只能走4个结果中的其中一个,所以,后手只有4条路都赢,我们才输! 都赢,都,所以用 & ,后手赢,我们就输,是个相反的结果,所以取非。
#include<bits/stdc++.h>
using namespace std;
bool a[10005];// 0 先手输
int main()
{
a[1]=0,a[2]=1,a[3]=0,a[4]=1,a[5]=0,a[6]=1,a[7]=1,a[8]=1;
for(int i = 9;i<=10000;++i)a[i] = !(a[i-1] && a[i-3] && a[i-7] && a[i-8]);
int t;scanf("%d",&t);
while(t--){
int x;scanf("%d",&x);
printf("%d\n",a[x]);
}
return 0;
}