hdu 1848 Fibonacci again and again sg值 博弈论

虽然今天队内赛又被大一小学弟惨虐,但是我发愤图强,学到了一个好东西:SG函数!这真是解博弈论水题的王道啊!!在这里推荐大家一个都知道,但是可能又不喜欢的,但是真的非常好的东西。。。。那就是百度文库。。。。只要不是具体的题目,一些概念性的,比如博弈论,二分图(包括二分图的各种算法),各种三分二分,数学公式,只要不是具体到题目,都讲的非常好的。下面讲正题:SG函数。

可能大家看了很多资料,还是有点迷迷糊糊,我争取用土的不能再土的白话给大家解释一下吧。博弈论最经常用的模型就是抓石头了,我们先知讨论一堆石头,多堆的先放一边。再定义一个集合A,定义一个运算meg(A),meg(A) = 不在A集合中最小的非负整数。比如集合A = {0,2,4,5};那么不在A集合中最小的非负整数就是1,再假如A = {1,2,3},那么不在集合A中最小的非负整数就是0,即meg(A) = 0;我们定义SG函数的意义。对于一堆石头,如果当前的石头数是i,SG(i) 如果等于0,那么表示面对剩下i个石头这个局面的人必定会输。如果sg(i)不等于0,那么面对剩下i个石头局面的人就会赢。那么怎么求sg值呢?

官方点说,sg[i] = meg(sg(y)|y是由x可以到达的局面)。假设有n个石头,我们每次只可以抓1个石头,3个石头,或者5个石头,对于一个石头的局面,我们是不是没有选择,只可以抓一个石头?那么由1这个局面只可以到达0这个局面,而sg[0] = 0(因为没有石头可以抓了肯定是输的嘛)。那么sg[1] = meg(sg[0]) = meg(0) = 1;对于有两个石头的局面,我们只可以抓一个,因为抓3个就变成负数了。。。所以剩两个石头的局面只可以到达剩一个石头的局面。sg[2] = meg(sg[1]) = meg(1) = 0;对于剩下3个石头的局面,我们的选择就多起来了。我们可以拿一个石头,也可以拿三个石头,也就是可以到达剩0个石头,剩2个石头的局面,那么sg[3] = meg(sg[0],sg[2]) = meg(0,0) = 1;sg[4] = meg(sg[3],sg[1]) = meg(1,1)

= 0;观察下,是不是sg值为0的都是输的局面?如果是多堆石头,比如三堆石头,数量分别为n,m,p。如果sg[n] ^  sg[m] ^ sg[p] = 0,那么这个局面就是输的,反之则是赢的。

最后给自己提个醒,位运算时!!!!一定!!!一定!!!要打括号!!!

下面上代码吧。

#include<stdio.h>

int fibo[20],sg[1005];

int main()
{
	int n,m,p;
	while(scanf("%d%d%d",&n,&m,&p) && (n || m || p))
	{
		int a,b,count = 1;
		fibo[0] = fibo[1] = 1;
		for(int i = 2;i < 20;++i)
			fibo[i] = fibo[i-1] + fibo[i-2];//先把斐波那契数列求出来
		sg[0] = 0;//0个石头肯定是输的局面
		for(i = 1;i < 1005;++i)
		{
			int tag[1005] = {0};//标记数组,用来求不在集合中最小的非负整数
			for(int j = 1;i - fibo[j] >= 0;++j)
				tag[sg[i-fibo[j]]] = 1;  //把求出来的sg值标记下
			for(j = 0;tag[j];++j);//求出没被标记的数,就是最小的,不在集合中的数
			sg[i] = j;
		}
		if((sg[n] ^ sg[m] ^ sg[p]) == 0)//千万记得位运算的时候一定要打括号!!!被坑了无数次了。。。。
			puts("Nacci");
		else
			puts("Fibo");
			
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/a549875231/article/details/10115475