Hduoj1848【博弈】

/*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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值