博弈算法——SG函数和SG定理(还有斐波那契和nim两个坑)

目录

一、斐波那契博弈:

二、巴什博奕(Bash Game):

三、威佐夫博弈:

四、尼姆博弈:

五、SG函数:


一、斐波那契博弈:

描述:有一堆物品,两人轮流取物品,先手最少取一个,至多无上限,但不能把物品取完,之后每次取的物品数不能超过上一次取的物品数的2倍且至少为一件,取走最后一件物品的人获胜。

结论:当 n是斐波那契数 时后手获胜(n为物品总数)。

原理:有两个智者——郭大彪和钟大锤,他们对于博弈已经烂熟于心了。所以每次都可以找到最好的对自己有利的走法。

          在这里,我们要借助一个定理——齐肯多夫定理,齐大爷说:“任何正整数可以表示为若干个不连续的斐波那契数之和。”至于为什么,我们剧场由于穷,请不到齐大爷来讲解,就不解释了,大家记住结论就好;还要借助斐波那契数列的性质——任意一个斐波那契数f[k]都小于f[k-1]的两倍,都大于f[k-n](n>1)的两倍

          接下来证明斐波那契后手必胜的原因(数学归纳):

               1)当n=2或3时,后手必胜显然成立;

              2)假设对k堆前的斐波那契可以确定后手必胜,当n=f[k+1]时,我们可以人为的把它分为它前面两个斐波那契数的堆,记为k堆和k-1堆,则对于k-1堆,后手必可取到最后一颗。下面我们分析一下后手最后取的石子数x的情况:如果先手第一次取的石子数y>=f[k-1]/3,则这小堆所剩的石子数小于2y,即后手可以直接取完,此时x=f[k-1]-y,则x<=2/3*f[k-1]。我们来比较一下2/3*f[k-1]与1/2*f[k]的大小。即4*f[k-1]与3*f[k]的大小,由数学归纳法不难得出,后者大。所以我们得到,x<1/2*f[k]。即后手取完k-1堆后,先手不能一下取完k堆,所以游戏规则没有改变,则由假设可知,对于k堆,后手仍能取到最后一颗,所以后手必胜。即i=k+1时,结论依然成立。

   非斐波那契后手必败先挖坑

典例:杭电2516

 

二、巴什博奕(Bash Game):

描述:只有一堆n个物品,两个人轮流从中取物,规定每次取数区间[s,m],当然如果少于s那么必须一次取完,最后取光者为胜。

结论:如果 0 后手获胜。

详解:假设大彪(先)和大锤(后)两人又开始进行一场巴什博奕。

           如果有(s,s+m]个物品,那么必然导致大锤的胜利:因为无论大彪取多少,都无法取完;而大锤后手就可以把剩下的都拿走。而对于每个n我们都可以用n=(s+m)*x+t表示(当t为0时大锤也必胜)。故而我们对t讨论:称0和(s,s+m]为大锤的有利局势,而[1,s]为大彪的有利局势。

            早在一开始聪明的大彪和大锤就已经知道了结果,所以无论一开始的局面对谁有利(假如对大锤有利,那么大彪先手取A,大锤每次取走(s+m-A)以维持自己的有利局势;假如对大彪有利,那么大彪先手取走s,此后开始针对大锤,取走(s+m-大锤取的数))。

           因为大彪和大锤都寻求自己的最有利状态,所以最终结果取决于t!

典例:杭电2897

 

三、威佐夫博弈:

描述:有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。

结论:若两堆物品的初始值为(x,y),令z=|y-x|;记 w=(int)[((sqrt(5)+1)/2)*z];若w==min(x,y),则后手获胜。

原理:在威佐夫博弈中我们引入一个概念——奇异局势。前几个奇异局势是(0,0)(1,2)(3,5)(4,7)(6,10)……

           容易看出,奇异局势即(a[k],b[k]),a为此前没有出现过的自然数,b[k]=a[k]+k(k为奇异局势的序号,从〇开始)。而奇异局势的公式表示为a[k] =(k(1+√5)/2),b[k[= a[k] +  k

           那么对于奇异局势,后手必胜;否则,先手必胜!为什么呢?

           因为按照所给出的两种操作,都只能使奇异局势变为非奇异局势,或使非奇异局势变为奇异局势;而且必然可以使奇异局势变为非奇异局势,或使非奇异局势变为奇异局势。

           我们再次有请锤(先)彪(后)两位智者来演示下:对于一个非奇异局势,大锤可以用一步使其变为奇异局势,而接下来,对于奇异局势,总有偶数次操作使其变为(0,0),从而导致大彪的失败。

典例:杭电1527、杭电2177

 

四、尼姆博弈:

描述:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜。

结论:把每堆物品数全部异或起来,如果得到的值为0,那么后手获胜。

原理:首先我们引入P、N两种状态,P即previous(先手必败)、N即next(后手必败)。有点迷哈,为什么要定义必败而不是必胜态~接下来几个定义:

                    1)无法移动(即最终目的)为P态

                    2)可以移动到P态的局面都是N态

                    3)所有移动都会导致N态的局面是P态(即P和N互相转换,不能由N\P一步到N\P)

            还有就是:对于任意一个数的二进制做减法,我们都可以1)使它的某一位由1到0,2)同时可以选择该其后一位数字。

           然后开始推导:已知每堆均为0是P(先手必败)态。

典例:杭电1850、杭电1849

五、SG函数:

描述:组合博弈的解决办法

结论:组合博弈的结果相当于各分博弈的结果的Nim和

         引入一个mex运算:它的作用是找出一堆数中还没有出现的首个自然数。例如:mex{1,2,3}=0、mex{0,1,5}=2;

        那么SG[x]=mex{S},S表示x后继状态的SG值的集合。例如x有后继状态a,b,c,那么SG[x]=mex{SG[a],SG[b],SG[c]}。而最终,S必然会成为空集,所以SG的终态为SG[x]=0(当且仅当x为必败点)。

         吐槽:叫个什么鬼的后继状态,害得我一直懵逼,其实就是n粒米饭吃了m粒,n-m称为n的后继状态。[○・`Д´・ ○]

典例:杭电1048

详解:这里再结合杭电1048讲解一下:

           对于任意一堆石子(n个):有斐波那契(下简称F[] )的取法(n<1000,所以注意上限)

           当x=0时,先手败,SG[0]=0;

           当x=1时,可以取走F[1],剩余0,所以0是1的后继状态,故SG[1]=mex{SG[0]}=mex{0}=1;

           当x=2时,可以取走F[1]或F[2],剩余1或0,所以0和1是2的后继状态,故SG[2]=mex{SG[0],SG[1] }=mex{0,1}=2;

           当x=3时,可以取走{1,2,3},剩余2,1或0,所以0和1,2是3的后继状态,故SG[3]=mex{0,1,2}=3;

           当x=4时,可以取走{1,2,3},剩余1,2或3,所以3和1,2是4的后继状态,故SG[4]=mex{1,2,3}=0;

           当x=5时,可以取走{1,2,3,5},剩余0,2,3或4,所以0,2,3和4是5的后继状态,故SG[5]=mex{0,2,3}=1;

           以此类推……

贴上代码:

#include 
#include 
#include 
using namespace std;
int F[20],SG[1010],S[1010];
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
void getSG(int n)
{
	int i,j;
	memset(SG,0,sizeof(SG));//
	for(i=1;i<=n;i++)//SG[0]必为0  故从1开始 
	{
		memset(S,0,sizeof(S));//每次斗殴要将上次的后继状态清空 
		for(j=0;F[j]<=i&&j<20;j++) S[SG[i-F[j]]]=1;//记录本次的后继状态 
		for(j=0;;j++) if(!S[j]) //模拟mex运算 如果某个自然数未出现 
		{
			SG[i]=j;//则记为该自然数。 
			break;//
		}
	}
}
int main()
{
	int n,m,k;
	F[0]=F[1]=1;
	for(int i=2;i<=16;i++) F[i]=F[i-1]+F[i-2];//构建可以取的方式 
	getSG(1000);//打表SG 
	while(cin>>m>>n>>k&&m)
	{
		if(SG[n]^SG[m]^SG[k]) printf("Fibo\n");
		else cout<<"Nacci\n"; 
	}
}

<写得头昏脑涨,有问题请谅解并联系在下>

你可能感兴趣的:(数据结构)