前提:
足够聪明,所以,条件注定了胜败。
博弈:
1.巴什博弈(Bash Game) 一堆石头两个人取,最多取m最少取1,取光者胜。
2.威佐夫博奕(Wythoff Game) 两堆石头轮流取,从一堆中取k个或者同时取k个,取光者胜。
3.尼姆博弈论(Nimm Game) n堆石头 n堆石头,每次取一堆,无限制,取光者胜。
两个状态:
P-position P指的是Previous上一个
N-position N指的是next下一个
position位置
定义:
上一个想赢,所以这个位置的人必输,即先手必输。为p状态(奇异局势)
相对于上一个人,他的下一个就是当前位置的,当前位置想赢,就是先手必赢。为N状态(非奇异局势)
一.巴什博弈(Bash Game)
n%(m+1)=0;先手必输(足够聪明)
else
先手取n%(m+1),后手必输。
二.威佐夫博奕(Wythoff Game)
必输局势(0,0)
所以相对于他,有(1,0)(2,0)(1,1)(2,2)等必赢
那么相对于这个,加上一个一次性取不完的条件(1,2)那么必输,不管怎么取都会回到上步比赢的状态。
然后递推:必输的状态有(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)
得出规律公式(百度百科有证明)
ak=[k*(1+√5)/2],bk=ak+k;
满足黄金分割,威佐夫博弈又叫黄金分割博弈
所以
a:b=√5-1:2时先手必输。
else
先手必赢
三.尼姆博弈论(Nimm Game)
很简单的取,假设有三堆石头(1,1,0),先手取1个,后手取另一个就一定能赢(先手必输策略)。同理假设有三堆石头(1,2,3)就可以分成(1,10,10,1)又是先手必输,很明显的可以看出来这就是异或关系“^”,当所有堆的石头异或为零时,就存在,相对应的,相同的石头个数。同理,假设异或结束后不为零,先手取掉这些就好了,这样异或就为零后手必输了。
四.斐波那契博弈(Fibonacci Nim Game)
一堆石子,两人轮流取,先取者第1次可以取任意多个,但不能全部取完,以后每次取的石子数不能超过上次取子数的2倍。取完者胜。
自行百度
主要是变形:
引入定理:
Sprague-Grundy定理(SG定理,又叫SG函数)
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数,很迷。
有1堆n个的石子,每次只能取{ 1, 3, 4 }个石子,先取完石子者胜利,那么各个数的SG值为多少?
SG[0]=0,f[]={1,3,4},
x=1 时,可以取走1 - f{1}个石子,剩余{0}个,所以 SG[1] = mex{ SG[0] }= mex{0} = 1;
x=2 时,可以取走2 - f{1}个石子,剩余{1}个,所以 SG[2] = mex{ SG[1] }= mex{1} = 0;
x=3 时,可以取走3 - f{1,3}个石子,剩余{2,0}个,所以 SG[3] = mex{SG[2],SG[0]} = mex{0,0} =1;
x=4 时,可以取走4- f{1,3,4}个石子,剩余{3,1,0}个,所以 SG[4] = mex{SG[3],SG[1],SG[0]} = mex{1,1,0} = 2;
x=5 时,可以取走5 - f{1,3,4}个石子,剩余{4,2,1}个,所以SG[5] = mex{SG[4],SG[2],SG[1]} =mex{2,0,1} = 3;
以此类推.....
X 0 1 2 3 4 5 6 7 8....
SG[x] 0 1 0 1 2 3 2 0 1....
步骤:
1、预处理 f 数组(当前通过哪些操作后可得到的状态)
2、使用另一个数组标记后继状态。
3、模拟 mex 运算,找最小值,赋给SG
4、重复2-3,完成过程。
什么意思呢,假设有一堆5个的石头,5可以变成4,2,1那种形式,连续的sg就是0,1,2,如果它想赢,那么他为什么不把他拿后的状态让对面输呢,因为他的SG有0啊,可以取到对面输。这样看,SG函数只有0,1,就应该够了,当然,这是因为只有一堆,如果是多堆:
继续看样例,
假设有三堆,3,4,5
SG[3]=1
SG[4]=2
SG[5]=3
想要赢怎么赢,让这些SG全部转化为0,不就赢了,然后是不是好熟悉,三堆,1,2,3,可以任意取,全取为0。这样就成了nimm博弈了。
一个计算SG函数的实例:
/*输入n,从1开始,每次乘以2~9的数,谁最先达到n谁胜*/
#include<iostream> #include<cstdio> #include<cstring> #include<set> #define mem(a,x) memset(a,x,sizeof(a)) using namespace std; typedef long long ll; const int N = 100000; int sg[N+4]; void solve() { sg[1] = 0; //败态 for (int i = 2;i <= N;++i) { set<int>s; for (int j = 2;j <= 9;++j) { int to = i/j; if (i%j)to++; s.insert(sg[to]);//i>=j*to 所以如果存在to态则i可转化为to态 (同时记录sg[]出现的值用于求mex)/到达i可以超过i } int g = 0; // (若to态没有必败态,则i必败)或者一直到(最小的不属于这个集合的非负整数)即sg[]没出现的那个值 while (s.count(g)){ ++g; } sg[i] = g; } for (int i = 0;i <= 9000;++i) { cout<<i<<" "<<sg[i]<<endl; } } int main() { solve(); return 0; }