Fibonacciagain and again
Time Limit: 1000/1000 MS(Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8754 Accepted Submission(s): 3637
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
本题很明显就是一个求SG值的题目,在把三个SG异或下即可
若异或值为非零值,先手必胜
若异或值为零,先手必败
博主整理了两种求SG值的方法
法一:
void init()
{
int i,j;
sg[0] = 0;
for(i=1; i<maxn; i++) //maxn为要处理的数据个数
{
memset(a,0,sizeof(a));
for(j=0; b[j]<=i; j++) //b数组里储存每次可取的值
a[sg[i-b[j]]] =1;
for(j=0; j<maxn; j++)
if(!a[j])
{
sg[i] = j;
break;
}
}
}
法二:
int mex(int p)
{
int i,t;
bool flag[maxn]={0};
for(i=0; i<maxm; i++) //maxm为可取数的个数
{
t=p-a[i]; //a数组里储存每次可取的值
if(t<0)
break;
if(sg[t]==-1) //sg数组初始化为-1
sg[t]=mex(t);
flag[sg[t]]=1;
}
for(i=0;; i++)
{
if(!flag[i])
return i;
}
}
若只有一堆石子,即不用求SG值(异或值),只要判断必胜必败态,那么还有一种写法(思路类似)
void init()
{
for(int i=1; i<maxn; i++)
{
int j,flag;
j=flag=0;
while(i-b[j]>=0) //b数组存每次可取的值
{
if(sg[i-b[j]]==0)
{
flag=1;
break;
}
j++;
}
sg[i]=flag;
}
}
本题的代码如下
法一:
#include <bits/stdc++.h>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
const int maxn = 1005;
const int maxm = 16;
const int mod = 9973;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
#define ll long long
#define rush() int T;scanf("%d",&T);while(T--)
int a[maxn],sg[maxn];
int b[maxm]= {1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597};
void init()
{
int i,j;
sg[0] = 0;
for(i=1; i<maxn; i++) //maxn为要处理的数据个数
{
memset(a,0,sizeof(a));
for(j=0; b[j]<=i; j++) //b数组里储存每次可取的值
a[sg[i-b[j]]] =1;
for(j=0; j<maxn; j++)
if(!a[j])
{
sg[i] = j;
break;
}
}
}
int main()
{
int m,n,p,t;
init();
while(~scanf("%d%d%d",&m,&n,&p)&&(m||n||p))
{
t = sg[m]^sg[n]^sg[p];
if(t)
printf("Fibo\n");
else
printf("Nacci\n");
}
return 0;
}
法二:
#include <bits/stdc++.h>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
const int maxn = 1005;
const int maxm = 16;
const int mod = 9973;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
#define ll long long
#define rush() int T;scanf("%d",&T);while(T--)
int a[maxm]= {1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597};
int f[maxn];
int mex(int p)
{
int i,t;
bool g[maxn]={0};
for(i=0; i<maxm; i++) //maxm为可取数的个数
{
t=p-a[i];
if(t<0)
break;
if(f[t]==-1)
f[t]=mex(t);
g[f[t]]=1;
}
for(i=0;; i++)
{
if(!g[i])
return i;
}
}
int main()
{
int m,n,p,t;
mst(f,-1);
while(~scanf("%d%d%d",&m,&n,&p)&&(m||n||p))
{
if(f[m]==-1)
f[m]=mex(m);
if(f[n]==-1)
f[n]=mex(n);
if(f[p]==-1)
f[p]=mex(p);
t = f[m]^f[n]^f[p];
if(t)
printf("Fibo\n");
else
printf("Nacci\n");
}
return 0;
}