两周前去学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` 所以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:打表 模板2:DFS 二,威佐夫博弈 0,游戏:有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。 1,直接结论: 假设两堆石子为(x,y)(x,y)(其中x 那么先手必败,当且仅当: (y-x)*(√5+1)/2==x 其中的\√5+1/2
//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;
}
}
}
}
//注意 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