博弈论初识

喜欢而已 提交于 2019-12-25 09:37:42

概念:

P——即必败点,某玩家位于此点,只要对方无失误,则必败;

N——即必胜点,某玩家位于此点,只要自己无失误,则必胜。

定理:

     一、 所有终结点都是必败点P(上游戏中,轮到谁拿牌,还剩0张牌的时候,此人就输了,因为无牌可取);

     二、所有一步能走到必败点P的就是N点;

  三、通过一步操作只能到N点的就是P点;

又即:只要当前状态可以转移到的状态中有一个是败态,那么当前状态就是胜态。如果当前状态可以转移到的所有状态都是胜态,那么当前状态就是败态。

 

Bash Game只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜。

 

1,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,

 

后取者都能够一次拿走剩余的物品,后者取胜。必败

 

2,法则:如果n=m+1)*r+s,(r为任意自然数,sm),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,

 

那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。

 

总之,要保持给对手留下(m+1)的倍数,就能最后获胜。必胜局

 

Wythoff Game:有两堆各若干个物品,每个人每次可以从一堆里取任意多的物品,或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,轮流取,最后取光者得胜

 

 

 

我们用(a[k]b[k])(a[k] b[k] ,k=012...,n)表示两堆物品的数量并称其为局势。

 

如果甲面对(00),那么甲已经输了,这种局势我们称为奇异局势。

 

首先列举人们已经发现的前几个奇异局势:(00)、(12)、(35)、(47)、(610)(813)、(915)、(1118)、(1220)。

 

通过观察发现: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}=3mex{2,3,5}=0mex{}=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~nSG函数值步骤如下:

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}

说了一大堆,也没懂多少。

就当作记录一下都看了些啥好了。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!