HDU1848 Fibonacci again and again【博弈】

来源:互联网 发布:迅雷mac版下载速度是0 编辑:程序博客网 时间:2024/05/29 11:53

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1848


题目大意:

二个人玩取石子游戏,一共有三堆石子,分别为m、n、p个。两个人轮流取石子,每次可以任选一堆石子,

然后取斐波那契数列中的f(n)个。每次都使用最优策略,先取完的人获胜。问:先手的人会赢还厚后手的人会

赢?


思路:

这是一道博弈题。

Fibo[] = {1,2,3,5,8,13,21,…}。根据题意每次只能取fibo[i]个。则:

1.如果只有1堆m个,而m是某个fibo[i],则m是必胜点。m = 1,2,3,5,8,13,21,…是必胜点。

可以看出来0,4就是必败点。如果从m中取走k个(k是某个fibo[i]),m-k是必败点的话,则m

是必胜点。比如说从6个中取走2个,6-2 = 4,4是必败点,则6是必胜点;从7个中取走3个,

7-3 = 4,4是必败点,则7是必胜点,同理9也是 必胜点。

当m = 10的时候,从m中无论取多少个都是必胜点。10-1=9,10-2=8,10-3=7,10-5=5,

10-8=2,则10是必败点。

这样子递推能求出只有1堆的时候,所有必胜点和必败点。

现在考虑一堆m个,一堆n个的情况。

如果m是必败点,n是必败点,则两堆组合(m,n)也是必败点。

这样可以知道(4,10)必败、(4,4)必败、(4,5)必胜。

这是因为:

必败 && 必败 == 必败

必胜 && 必胜 == 必胜

两堆状态一致。

推广为三堆状态:

必败 && 必败 && 必败 == 必败

必胜 && 必胜 && 必胜 == 必胜

设必败点为0,必胜点为1,三堆的状态分别为:sg[N],sg[M],sg[P],如果开始

三堆都为必败点,则先手必败,相反的话,则必胜。判断三堆都为必败点,只要三

堆状态都为0即可,即sg[N]^sg[M]^sg[P] == 0。

参考博客:http://hi.baidu.com/king___haha/item/542a071140107f9598ce337c


AC代码:

#include<iostream>#include<algorithm>#include<cstdio>#include<cstring>using namespace std;int f[1100],sg[1100],b[1100];int main(){    f[1] = 1,f[2] = 2;    for(int i = 3; ; ++i)    {        f[i] = f[i-1] + f[i-2];        if(f[i] >= 1000)            break;    }    sg[0] = 0,sg[1] = 1;    for(int i = 1; i <= 1000; ++i)    {        memset(b,0,sizeof(b));        for(int j = 1; f[j] <= i; ++j)            b[sg[i-f[j]]] = 1;        for(int j = 0; j <= 1000; ++j)        {            if(!b[j])            {                sg[i] = j;                break;            }        }    }    int N,M,P;    while(cin >> N >> M >> P && (N||M||P))    {        if(sg[N]^sg[M]^sg[P])            cout << "Fibo" << endl;        else            cout << "Nacci" << endl;    }    return 0;}


0 0