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; }