两周前去学nim和sg函数,但是,又又又忘了QAQ
一,nim博弈
0,对nim游戏的定义是:两人,有若干堆石子,每堆石子数量是有限的(a1,a2,...,an),合法的移动时候:选择一堆石子并拿走若干颗(可以全拿,不能不拿)。如果轮到某人时,已无子可取,则为败,另一人为胜。
1,设P-position为先手必败(当前态为必败态)/后手可保证必胜;
2,设N-position先手可保证必胜(当前态为必胜态);
3,设游戏中无法进行任何移动的局面(terminal position)为P-position;
4,可以移动到P-position的局面是N-position(性质);
5,所有移动都导致N-position的局面是P-position(性质);
6,对nim有结论:对于一个nim游戏的局面(a1,a2,...,an),它是P-position当且仅当a1^a2^...^an==0(即,a1^a2^...^an!=0 为N-position);
7,判断6的正确性:
(1),对于terminal position,a1=a2=...=an=0,有a1^a2^...^an=0,而对于定义terminal potion为P-position,成立。
(2),假设6成立,则需满足N-position一定能移动到P-position(第4点),
N-position为 a1^a2^...^an!=0 ,设 a1^a2^...^an!=k (k!=0),则一定存在某个ai,它的二进制表示的最高位1为k而进制表示的最高位1,在这时,当前 将ai(通过取石子)的值,变换为ai`=ai^k,则有:a1^a2^...^an^k=k^k=0,在假设6成立下性质满足。
(3),假设6成立,则需满足P-position一定无法使下一个状态为P-position(第5点),
P-position为 a1^a2^...^ai^...^an==0 ,则,显然 a1^a2^...^ai`^...^an!=0(ai`<ai)。
所以6的正确性显然。
8,进一步,假设nim游戏有:每次最多只能取k个,的限制,则:将每堆石子数mod(k+1);。
9,SG函数:Sprague-Grundy函数:详见这个博客
首先定义mex(局外最小序数)运算,对于一个集合S,mex(S)为,不是S的元素的最小一个序数(非负)。如:mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负。
对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Garundy函数g:g(x)=mex{ g(y) | y是x的后继 }。
且定义:(1)叶子节点(terminal potsition)的SG值为0。
则有:(2)对于一个g(x)==0的顶点x,它的所有后继y都满足g(y)!=0。
(3)对于一个g(x)!=0的顶点x,它必有一个后继y满足g(y)==0。
则,由上面3点,可知,顶点x所代表的position为P-position当且仅当g(x)==0(即,g(x)!=0为N-position)
详见博客= =
当面对由n个游戏组合成的一个游戏时,只需对于每个游戏找出它每个局面的SG值的方法,就可以把SG值全部看出nim的石子堆,再由nim的必胜策略判断必胜局面。
SG值根据mex函数确定,具体方法为打表orDFS。
10,原文链接:https://blog.csdn.net/strangedbly/article/details/51137432
解题模型:
(1)把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。即sg(G)=sg(G1)^sg(G2)^...^sg(Gn)。
(2)分别考虑没一个子游戏,计算其SG值。
SG值的计算方法:(重点)
1,可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);
2,可选步数为任意步,SG(x) = x;
3,可选步数为一系列不连续的数,用模板计算。
模板1:打表
//f[]:可以取走的石子个数 //sg[]:0~n的SG函数值 //hash[]:mex{} int f[N],sg[N],hash[N]; void getSG(int n) { int i,j; memset(sg,0,sizeof(sg)); for(i=1;i<=n;i++) { memset(hash,0,sizeof(hash)); for(j=1;f[j]<=i;j++) hash[sg[i-f[j]]]=1; for(j=0;j<=n;j++) //求mes{}中未出现的最小的非负整数 { if(hash[j]==0) { sg[i]=j; break; } } } }
模板2:DFS
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍 //n是集合s的大小 S[i]是定义的特殊取法规则的数组 int s[110],sg[10010],n; int SG_dfs(int x) { int i; if(sg[x]!=-1) return sg[x]; bool vis[110]; memset(vis,0,sizeof(vis)); for(i=0;i<n;i++) { if(x>=s[i]) { SG_dfs(x-s[i]); vis[sg[x-s[i]]]=1; } } int e; for(i=0;;i++) if(!vis[i]) { e=i; break; } return sg[x]=e; }
二,威佐夫博弈
0,游戏:有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。
1,直接结论:
假设两堆石子为 (x,y)(其中x<y)
那么先手必败,当且仅当:
(y-x)*(sqrt(5)+1)/2==x
其中的 (sqrt(5)+1)/2 实际就是1.618,黄金分割数。