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代码(打表):
AC代码(深搜):
解决方法:
本题是多堆Nim游戏的一个简单的变形,多堆Nim游戏经典的解法是利用SG定理来求解
由于第16个斐波那契数为987,第17个斐波那契数为1597,所以只需要求出前16个斐波那契数
优先使用打表法,如果打表法无法使用则使用搜索法
注意代表取法的数组(如fib)需要按由大到小排序
任何一个大学生对菲波那契数列(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)需要按由大到小排序
相关文章推荐
- HDoj-1848-Fibonacci again and again-博弈-SG函数
- hdoj 1848 Fibonacci again and again(组合博弈, sg函数)
- HDOJ 1848.Fibonacci again and again SG函数
- HDU 1848 Fibonacci again and again(SG函数)
- [ACM] hdu 1848 Fibonacci again and again(Nim博弈 SG函数)
- [ACM] hdu 1848 Fibonacci again and again(Nim博弈 SG函数)
- (SG函数)Fibonacci again and again--HDOJ
- hdu1848——Fibonacci again and again(SG函数)
- HDOJ 1848 Fibonacci again and again
- hdu 1848 Fibonacci again and again(sg函数的简单应用 模板题 )
- Hdu1848 Fibonacci again and again(sg函数)
- HDU 1848 Fibonacci again and again(学习sg函数后写出来的第一个题)
- HDOJ题目1848Fibonacci again and again(sg博弈,打表模板)
- 【博弈论】【SG函数】hdu1848 Fibonacci again and again
- HDU_1848 Fibonacci again and again(SG函数应用)
- HDU 1848 Fibonacci again and again【博弈:SG函数】
- hdu 1848 Fibonacci again and again (sg函数)
- hdu 1848 Fibonacci again and again(SG函数)
- HDU 1848 Fibonacci again and again(博弈论:sg函数)
- HDOJ 1760 Fibonacci again and again 博弈 SG函数