/*Fibonacci again and again Time Limit : 1000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 3 Accepted Submission(s) : 1 Font: Times New Roman | Verdana | Georgia Font Size: ← → Problem Description 任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的: F(1)=1; F(2)=2; F(n)=F(n-1)+F(n-2)(n>=3); 所以,1,2,3,5,8,13……就是菲波那契数列。 在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。 今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下: 1、 这是一个二人游戏; 2、 一共有3堆石子,数量分别是m, n, p个; 3、 两人轮流走; 4、 每走一步可以选择任意一堆石子,然后取走f个; 5、 f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量); 6、 最先取光所有石子的人为胜者; 假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。 Input 输入数据包含多个测试用例,每个测试用例占一行,包含3个整数m,n,p(1<=m,n,p<=1000)。 m=n=p=0则表示输入结束。 Output 如果先手的人能赢,请输出“Fibo”,否则请输出“Nacci”,每个实例的输出占一行。 Sample Input 1 1 1 1 4 1 0 0 0 Sample Output Fibo Nacci Author lcy Source ACM Short Term Exam_2007/12/13 */ #include<stdio.h> #include<string.h> int f[17], sg[1001], b[1001]; void getsg()//计算等价类数 { memset(sg,0,sizeof(sg));//sg[0] = 0 0的等价类数为0 for(int i=1;i<=1000;i++)//推出1~1000的等价类数 { memset(b,true,sizeof(b));//标记等价类数 for(int j=0;j<15;j++) { if(i<f[j]) break; b[sg[i-f[j]]]=false;//取出斐波那契数后的等价类数标记 } for(int j=0;j<=1000;j++) if(b[j])// 寻找等价类数中未出现过的最小数(等价类数) { sg[i]=j; break; } } } int main() { int i, j, k, m, n, p; f[1] = 2; f[0] = 1; for(i = 2; i < 17; ++i)//建立斐波那契 f[i] = f[i-1] + f[i-2]; getsg(); while( scanf("%d%d%d", &m, &n, &p) != EOF && (m || n || p) ) { k = 0; k = k ^ sg[m] ^ sg[n] ^ sg[p]; if(k) printf("Fibo\n"); else printf("Nacci\n"); } return 0; }
题意:给出三堆石子,两个人轮流取石子,最后将石子取完的人胜。取石子规则:每次只能取其中一堆石子,每次取的个数为斐波那契数中的某个数。
思路:首先考虑一堆石子的情况,如果石子的个数为斐波那契数,则先取得人必胜,如果不是,则反过来考虑,将石子的个数取走斐波那契个数(考虑所有能取得斐波那契数)后为必胜,则该个数为必败。当石堆分为三堆时,这里就有了一个等价类数的概念:
只有1堆时所有必败点都和0等价,我们说只有1堆时所有必败点是第0类的或他的等价类数是0;
2堆 (1,5) 是必败的,5和1等价,和1等价的m(比如前面的5)叫做他的等价类数是1
2堆时的情况用 w2[m1][m2] 表示,w2[m1][m2]=0表示 (m1,m2)必败,
w2[m1][m2]=1表示 (m1,m2)必胜
等价类数的算法
E[0]=0;
等价类数 E[i] 的算法:从i个中取走 fib[1],fib[2],...,fib[j]<=i 个后剩下
i-fib[1], i-fib[2],..., i-fib[j]个
他们的等价类数中没有出现的最小数就是i的等价类数
例如 i=1,取走fib[1]=1个 i-fib[1]=0,0的等价类数是0,没有出现的最小数就是1
E[1]=1;
例如 i=2,取走fib[1]=1个 i-fib[1]=1,取走fib[2]=2个 i-fib[1]=0,
1和0的等价类数是1,0,没有出现的最小数就是2
E[2]=2;
例如 i=3,取走fib[1]=1个 i-fib[1]=2,取走fib[2]=2个 i-fib[1]=1,取走fib[3]=3个 i-fib[1]=0,
2,1和0的等价类数是2,1,0,没有出现的最小数就是3
E[3]=3;
例如 i=4,取走fib[1,2,3]=1,2,3个 剩下3,2,1,没有出现的最小数就是0
E[4]=0; 4是必败点
例如 i=5,取走fib[1,2,3,4]=1,2,3,5个 剩下4,3,2,0,等价类数是 0,3,2,0没有出现的最小数就是1
E[5]=1;
最后一个公式s=E[m]^E[n]^E[p]若s=0则为必败,否则为必胜。
大神原文链接:http://hi.baidu.com/king___haha/item/542a071140107f9598ce337c