1. 程式人生 > >HDU 1848(SG函式)

HDU 1848(SG函式)

傳送門

題面:

任何一個大學生對菲波那契數列(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函式xor起來即可判斷勝負關係。

    因此現在我們只需要處理出SG函式即可。

    對於任意狀態 x ,定義 SG(x) = mex(S),其中 S 是 x 後繼狀態的SG函式值的集合

    如 x 有三個後繼狀態分別為 SG(a),SG(b),SG(c),那麼。 這樣 集合S 的終態必然是空集,所以SG函式的終態為 SG(x) = 0,當且僅當 x 為必敗點P時。

    對於本題,任意一個狀態的SG函式為:

    因此我們只需要分別列舉x以及斐波那契數y,並模擬求出mex{}的值即可。複雜度接近於O(n)。

程式碼:

#include <bits/stdc++.h.>
#define maxn 1005
using namespace std;
int Fib[25];
int vis[maxn];
int SG[maxn];
void init(){//預處理出每一個狀態的SG函式
    Fib[1]=1;
    Fib[2]=1;
    for(int i=3;i<=18;i++){
        Fib[i]=Fib[i-1]+Fib[i-2];
    }
    for(int i=0;i<=1001;i++){
        memset(vis,false,sizeof(vis));
        for(int j=1;Fib[j]<=i;j++){
            vis[SG[i-Fib[j]]]=true;
        }
        for(int j=0;;j++){
            if(!vis[j]){
                 SG[i]=j;
                 break;
            }
        }
    }
}
int main()
{
    init();
    int m,n,p;
    while(~scanf("%d%d%d",&m,&n,&p)){
        if(m==0) break;
        if(SG[m]^SG[n]^SG[p]){//根據SG定理求解答案
            puts("Fibo");
        }
        else puts("Nacci");
    }
    return 0;
}