1.排石头游戏
给你一排n个石头,每次只能取一个或两个,求必胜策略
先讨论石头是奇数还是偶数,奇数就从中间拿一个,然后,对手拿几个,你就从与它对应的位置上拿几个。偶数的话就从中间拿2个。
2.巴什博奕(Bash Game)
只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
n=s+(m+1)*k
先手拿s个,然后对手拿l个,你就拿m+1-l个。
3.威佐夫博奕(Wythoff Game)
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这个游戏所包含的数学原理很深,先手必败 ,公式是a=i*(1+sqrt(5))/2(I=0~n);b=a+n;
参考编程之美,里面将原理讲的非常详细。
公式证明就不写了,主要就是证明a,b包含了整个自然数集,并且不重复。1/a+1/b=1。a-b=1。联立方程组,求解。这里要引用一个定理,参考博客https://blog.csdn.net/maththinker/article/details/47757445
这是直接公式求解:
bool nim(int x, int y) {
double a, b;
a = (1 + sqrt(5.0)) / 2;
b = (3 + sqrt(5.0)) / 2;
if (x == y)return true;
if (x > y)swap(x, y);
if (y - x == (long)floor(x*a))return false;
return true;
}
这是迭代出所以不安全局面:
#include<iostream>
#include<string.h>
#include<sstream>
#include<set>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<Windows.h>
#include<math.h>
using namespace std;
const int maxn = 100000;
bool nim(int x, int y) {
if (x == y)return true;//先手必胜,这是安全局面
if (x > y)swap(x, y);
if (x == 1 && y == 2)return false; //先手必输,这是不安全局面
vector<int> A;
A.push_back(2);
int n = 1;
int delta = 1;
int mp[maxn];
memset(mp, 0, sizeof(mp));
mp[2] = 1;
while (x > n) {
while (mp[++n]);
delta++;
A.push_back(n + delta);
mp[n + delta] = 1;
}
if (x != n || !mp[y])return true;//已知的不安全局面中没有它
else return false;
}
int main(){
cout << (nim(6, 10) ? "win" : "lose") << endl;
system("pause");
return 0;
}
4.nim堆
有n堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
m1 ^ m2 ^ m3 ^ m4…=0表示先手必败
改变一个值肯定使它们异或不为0 。如果一开始不为0,那么就找到这个改变的数使它变为0。
5思考题
这个思考题就比较有意思了,但是不难,不妨先将1,2,3,4,5,6,7等较小的局面给推出来。
我们可以知道n>1,n=2时,我拿只能拿一个,你就可以拿完,显然是必输局,暂称为N。n=3时,显然也是N局,n=4,显然不是N局,暂称为Y局,为了保证不一开始就输,我只能拿1个,你为了不输也只能拿一个,然后我就能拿完。n=5时显然是必输局势,它只有两种拿法,一种是拿一个,拿完后局势就变成4,也就是你的Y局,拿2个,那就接输了。所有我们不难发现一个规律,将前一个N局势称为Nn-1,(n-Nn-1)*2<Nn-1,让后者不能一次拿光并且你处在N局势,符合这个条件就是Y局势。N局势显然就是不能转换到前一个N局势的数。
主要难点就在于对于状态转换的处理。如何将局势转换成必胜局势,让对手必输。
总结
总的来说,博弈论游戏有很多,也没人能将所有公式都列出来,也不必将所有公式都背下来,关键是如何分析问题,如何设计一个方法来解决它,更深层次就可以推导出它的数学原理。