您的位置:首页 > 大数据 > 人工智能

HDOj-1848 Fibonacci again and again-SG函数

2018-03-25 10:33 393 查看
问题描述:

任何一个大学生对菲波那契数列(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、 最先取光所有石子的人为胜者;

假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。

AC代码(打表):

int fib[20];//前16个斐波那契数
int sg[1010];
bool prev[1010];//标记后继状态
int n,m,p;
void get_sg(int n)
{
for(int i = 1; i <= n; ++i)//对于每一个局面i,注意这是逆推的过程
{
memset(prev,0,sizeof(prev));//重置其后继状态
for(int j = 1; j <= 16; ++j)//对于16个斐波那契数
if(i - fib[j] >= 0)//如果值合法
prev[sg[i - fib[j]]] = true;//根据SG函数的定义,标记的是后继状态的SG函数值
for(int j = 0; ; ++j)//求mex值,从小到大
{
if(prev[j] == false)//如果j没出现
{
sg[i] = j;//赋值
break;//退出循环
}
}
}
return;
}
int main()
{
//求斐波那契数
fib[0] = 1;
fib[1] = 1;
for(int i = 2; i <= 16; ++i)
fib[i] = fib[i - 1] + fib[i - 2];
get_sg(1000);//求SG函数,1-1000
while(cin >> n >> m >> p)
{
if(n == 0 && m == 0 && p == 0)
break;
if((sg
^ sg[m] ^ sg[p]) != 0)//SG定理,异或和非0则先手必胜,注意要加小括号
puts("Fibo");
else//异或和为0则先手必败
puts("Nacci");
}
return 0;
}


AC代码(深搜):

int fib[20];//前16个斐波那契数
int sg[1010];
int n,m,p;
void get_sg(int x)
{
if(sg[x] != -1)//如果被搜过
return;//就返回
bool book[1010];//标记后继状态的SG函数值,是局部数组
memset(book,0,sizeof(book));//重置
for(int i = 1; i <= 16; ++i)//对于每一个斐波那契数
{
if(x - fib[i] >= 0)//如果值合法
{
get_sg(x - fib[i]);//搜索
book[sg[x - fib[i]]] = true;//回溯时标记
}
}
for(int i = 0; ; ++i)//求mex函数值
{
if(book[i] == 0)
{
sg[x] = i;//赋值
return;//返回
}
}
}
int main()
{
//求斐波那契数
fib[0] = 1;
fib[1] = 1;
for(int i = 2; i <= 16; ++i)
fib[i] = fib[i - 1] + fib[i - 2];
memset(sg,-1,sizeof(sg));//初始化
for(int i = 1; i <= 1000; ++i)
get_sg(i);//求SG函数,i
while(cin >> n >> m >> p)
{
if(n == 0 && m == 0 && p == 0)
break;
if((sg
^ sg[m] ^ sg[p]) != 0)//SG定理,异或和非0则先手必胜,注意要加小括号
puts("Fibo");
else//异或和为0则先手必败
puts("Nacci");
}
return 0;
}


解决方法:

本题是多堆Nim游戏的一个简单的变形,多堆Nim游戏经典的解法是利用SG定理来求解

由于第16个斐波那契数为987,第17个斐波那契数为1597,所以只需要求出前16个斐波那契数

优先使用打表法,如果打表法无法使用则使用搜索法

注意代表取法的数组(如fib)需要按由大到小排序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: