简单博弈论https://blog.csdn.net/pengwill97/article/details/76796070#commentBox
sg函数讲解
https://www.cnblogs.com/DWVictor/p/10235851.html
NIM(尼姆)博弈
有三堆石子, 每堆有若干个,两个人轮流从某一堆中任取石子,每次至少取一个,多者不限,最后取完者获胜。
先手必败:A(XOR)B(XOR)C = 0。
否则先手必胜。可以推广到n堆。
巴什博弈
只有一堆n个石子,两个人轮流从这堆石子中取石子,规定每次至少取一个,最多取m个,最后取完的人获胜。
当 n % (m+1) = 0时,后手必胜。
当 n % (m+1) != 0时,先手必胜。
斐波那契博弈
有一堆个数为n的石子,游戏双方轮流取石子,满足:
先手不能再第一次把所有石子取完;之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间,包括1和对手取的石子数的2倍。取最后石子的人为赢家。
当且仅当n不是Fibonacci数时,先手必胜。换句话说,先手必败构成Fibonacci数列。
威佐夫博弈
有两堆各若干个石子,两个人轮流从某一堆或者两堆中取同样多的物品,规定每次至少取一个,多着不限,最后取完石子的人获胜。
先手必败的情况:对于一个奇异局势(A,B), A = 下取整[ (B-A) * 1.618 ],更准确的说,1.618 = (sqrt(5) + 1) / 2.
否则先手必胜,注意威佐夫博弈奇异局势的判定一定要用sqrt算出来,不能直接带1.618
sg函数模板
游戏和的SG函数等于各个游戏SG函数的异或和。
如果结果为0说明先手必败,否则先手必胜。
一定要把s数组开的大一点,至少比后继状态的数大。
一定要从小到大计算sg值
//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
int f[n],sg[maxn],s[maxn];
void getsg(int n)
{
memset(sg,0,sizeof(sg));
//因为SG[0]始终等于0,所以i从1开始
for(int i = 1; i <= n; i++)
{
//每一次都要将上一状态 的 后继集合 重置
memset(s,0,sizeof(s));
for(int j = 0; f[j] <= i && j <= n; j++)
s[sg[i-f[j]]] = 1; //将后继状态的SG函数值进行标记 能够到达的状态
for(int j = 0;; j++)
if(!s[j]) //查询当前后继状态SG值中最小的非零值
{
sg[i] = j;
break;
}
}
}