博弈问题
问题需要满足四个性质:
- 题目描述一般为A ,B 2人做游戏
- A B交替进行某种游戏规定的操作,每操作一次,选手可以在有限的操作(操作必须合法)集合中任选一种。
- 对于游戏的任何一种可能的局面,合法的操作集合只取决于这个局面本身,不取决于其它因素(跟选手,以前的所有操作无关)
- 如果当前选手无法进行合法的操作,则为负
EG.
这是一个二人游戏,一共有3堆石子,数量分别是m, n, p个,两人轮流走, 每走一步可以选择任意一堆石子,然后取走f个, f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量), 最先取光所有石子的人为胜者,假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢, 如果先手的人能赢,请输出“Fibo”,否则请输出“Nacci”,每个实例的输出占一行。
求关于状态的SG函数,当函数值不为0时先手获胜,否则后手。
关于SG函数参见
SG函数与SG定理
如果问题有多个子游戏,则取其SG值相异或
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1e3 + 5;
int f[maxn], sg[maxn], S[maxn];
int init()
{
f[1] = 1;
f[2] = 2;
int i;
for( i = 3; i <= maxn; i++) {
f[i] = f[i-1] + f[i-2];
if(f[i] > maxn)break;
}
return i;
}
void get_sg(int n)
{
for(int i = 1; i <= n; i++) //从1枚举所有状态
{
memset(S, 0, sizeof(S)); //计算mex的
for(int j = 1; f[j] <= i; j++) //枚举这个状态所有可能到达的状态
{
S[sg[i-f[j]]] = 1; // 计算能到达的状态的sg是否出现过
}
for(j = 0;;++j){
if(!S[j]){
SG[i] = j;
break;
}
}
}
int main()
{
int m, n, p;
get_sg(init());
while(cin >> m >> n >> p, m+n+p)
{
puts(sg[n]^sg[m]^sg[p] ? "Fibo" : "Nacci");
}
return 0;
}