概念:
P点——即必败点,某玩家位于此点,只要对方无失误,则必败;
N点——即必胜点,某玩家位于此点,只要自己无失误,则必胜。
定理:
一、 所有终结点都是必败点P(上游戏中,轮到谁拿牌,还剩0张牌的时候,此人就输了,因为无牌可取);
二、所有一步能走到必败点P的就是N点;
三、通过一步操作只能到N点的就是P点;
又即:只要当前状态可以转移到的状态中有一个是败态,那么当前状态就是胜态。如果当前状态可以转移到的所有状态都是胜态,那么当前状态就是败态。
Bash Game:只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜。
1,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,
后取者都能够一次拿走剩余的物品,后者取胜。必败
2,法则:如果n=(m+1)*r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,
那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。
总之,要保持给对手留下(m+1)的倍数,就能最后获胜。必胜局
Wythoff Game:有两堆各若干个物品,每个人每次可以从一堆里取任意多的物品,或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,轮流取,最后取光者得胜
我们用(a[k],b[k])(a[k] ≤ b[k] ,k=0,1,2,...,n)表示两堆物品的数量并称其为局势。
如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。
首先列举人们已经发现的前几个奇异局势:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)(8,13)、(9,15)、(11,18)、(12,20)。
通过观察发现:a[0]=b[0]=0,a[k]是未在前面出现过的最小自然数,而 b[k]= a[k] + k。
4,奇异局势有如下三条性质:
1)任何自然数都包含且仅包含在一个奇异局势中。
2)任意操作都可以使奇异局势变为非奇异局势。
3)必有一种操作可以使非奇异局势变为奇异局势。
5,奇异局势公式:
a[k]=[k*(1+√5)/2],b[k]=a[k]+k。
(k=0,1,2......,[ ]表示取整)
有趣的是,式中的(1+√5)/2正是黄金分割比例。
6,判断
可以看出,如果两人都采取正确的操作,那么对于非奇异局势,先拿者必胜,对于奇异局势,
先拿者必败。
1 #include <stdio.h> 2 3 #include <math.h> 4 5 const double Gsr=(1+sqrt(5.0))/2; 6 7 void swap(int &a,int &b) 8 9 { 10 11 int t=b; 12 13 b=a; 14 15 a=t; 16 17 } 18 19 int main() 20 21 { 22 23 int a,b; 24 25 while(~scanf("%d%d",&a,&b)) 26 27 { 28 29 if(a>b) 30 31 swap(a,b); 32 33 if(a == (int)(Gsr*(b-a))) //奇异局势,先拿者输 34 35 puts("First Lose"); 36 37 else 38 39 puts("First Win"); 40 41 } 42 43 return 0; 44 45 }
Sprague-Grundy定理(SG定理):
游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。而Bouton定理就是Sprague-Grundy定理在Nim游戏中的直接应用,因为单堆的Nim游戏 SG函数满足 SG(x) = x。
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于任意状态 x , 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 这样 集合S 的终态必然是空集,所以SG函数的终态为 SG(x) = 0,当且仅当 x 为必败点P时。
如:6张扑克牌两名玩家轮流取,每次只能取1张或两张,谁先取完,谁先获胜。
终结点(剩下0张牌)必定是必败点。
剩下一张牌的时候,必胜;
剩下两张牌的时候,必胜;
剩下三张牌的时候,无论取一张或者两张,必败;
剩下四张牌的时候,可以只取一张,让对方遇到只剩三张牌的情况;
剩下五张牌的时候,可以只取两张,让对方遇到只剩三张牌的情况;
剩下六张牌的时候,无论取一张还是两张,对方都可以让你遇到只剩三张牌的情况。所以先手必输。
标记胜败点,则有
0 1 2 3 4 5 6
P N N P N N P
发现是有规律的。
记剩余x张牌为状态x,剩余0张牌时,SG[0]=mex{}=0;
x=1,可以取1-f{1}张牌,剩余0个,SG[1]=mex{SG[0]}=mex{0}=1;
x=2,可以取2-f{1,2}张牌,剩余1,0个,SG[2]=mex{SG[1],SG[0]}=mex{0,1}=2;
x=3,可以取3-f{1,2}张牌,剩余2,1个,SG[3]=mex{SG[1],SG[2]}=mex{1,2}=0;
x=4,可以取4-f{1,2}张牌,剩余3,2个,SG[1]=mex{SG[3],SG[2]}=mex{0,2}=1;
x=5,可以取5-f{1,2}张牌,剩余4,3个,SG[1]=mex{SG[4],SG[3]}=mex{0,1}=2;
x=6,可以取6-f{1,2}张牌,剩余5,4个,SG[1]=mex{SG[5],SG[4]}=mex{1,2}=0;
可以列出
0 1 2 3 4 5 6
0 1 2 0 1 2 0
由上述实例我们就可以得到SG函数值求解步骤,那么计算1~n的SG函数值步骤如下:
1、使用 数组f 将 可改变当前状态 的方式记录下来。
2、然后我们使用 另一个数组 将当前状态x 的后继状态标记。
3、最后模拟mex运算,也就是我们在标记值中 搜索 未被标记值 的最小值,将其赋值给SG(x)。
4、我们不断的重复 2 - 3 的步骤,就完成了 计算1~n 的函数值。
SG[6]为0则先手必输,否则先手必胜。
有N堆物品,每堆有M[i](1 <= i <= N)个物品,两个人轮流从任意一堆上取任意多的物品,最后取光者胜。两人都采取最优策略,问,是先手赢还是后手赢?
定理(亦是结论):如果SG[M[1]] xor SG[M[2]] xor SG[M[3]] xor …… xor SG[M[N]] == 0,那么先手输,否则先手赢。(xor是位运算中的异或操作)。
1 //f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理 2 //SG[]:0~n的SG函数值 3 //S[]:为x后继状态的集合 4 int f[N],SG[MAXN],S[MAXN]; 5 void getSG(int n){ 6 int i,j; 7 memset(SG,0,sizeof(SG)); 8 //因为SG[0]始终等于0,所以i从1开始 9 for(i = 1; i <= n; i++){ 10 //每一次都要将上一状态 的 后继集合 重置 11 memset(S,0,sizeof(S)); 12 for(j = 0; f[j] <= i && j <= N; j++) 13 S[SG[i-f[j]]] = 1; //将后继状态的SG函数值进行标记 14 for(j = 0;; j++) if(!S[j]){ //查询当前后继状态SG值中最小的非零值 15 SG[i] = j; 16 break; 17 } 18 }19}
说了一大堆,也没懂多少。
就当作记录一下都看了些啥好了。
来源:https://www.cnblogs.com/Jiiiin/p/8680807.html