组合博弈入门(题目练习及代码解析)

简单组合博弈模型详解: http://www.cnblogs.com/acm1314/p/4647034.html

(1)巴什博奕(Bash Game):只有一堆n个物品,两个人轮流从这堆物品中取物,规
定每次至少取一个,最多取m个。最后取光者得胜。

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2188

巴什博弈模板题。

#include<cstdio>
int main()
{
    int T, n, m;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        if(n%(m+1)==0) printf("Rabbit
");
        else printf("Grass
");
    }
    return 0;
}
View Code

 

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4764(裸模)

#include<cstdio>
int main()
{
    int n, k;
    while(scanf("%d%d", &n, &k)!=EOF&&(n||k))
    {
        if((n-1)%(k+1)==0) printf("Jiang
");
        else printf("Tang
");
    }
    return 0;
}
View Code

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2897(稍微变形)

#include<cstdio>
int main()
{
    int n, p, q;
    while(scanf("%d%d%d", &n, &p, &q)!=EOF)
    {
        int left = n%(p+q);
        if(left<=p&&left>0)//可能刚好整除,此时为WIN 
            printf("LOST
");
        else
            printf("WIN
");
    }
    return 0;
}
View Code

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2149(稍微变形)

#include<cstdio>
int main()
{
    int n, m;
    while(scanf("%d%d", &m, &n)!=EOF)
    {
        int ans = 0;
        ans=m%(n+1);
        if(ans==0)
            printf("none
");
        else if(m>=n)
            printf("%d
", ans);
        else if(m<n)
        {
            printf("%d", m);
            for(int i=m+1; i<=n; i++)
            printf(" %d", i);
            printf("
");
        }
    }
    return 0;
}
View Code

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1517

这道题是巴什博弈的小小变形。 你会发现, 对于这个问题。

如果输入是 2 ~ 9 ,因为Stan 是先手,所以Stan 必胜
如果输入是 10~18 ,因为Ollie 是后手,不管第一次Stan 乘的是什么,Stan肯定在 2 ~ 9 之间,
如果Stan乘以 2 ,那么Ollie就乘以 9 ,就到18了,如果Stan乘以 9 ,
那么Ollie乘以大于1的数都都能超过 10 ~ 18 中的任何一个数。Ollie 必胜
如果输入是 19 ~ 162,那么这个范围是 Stan 的必胜态
如果输入是 163 ~ 324 ,这是又是Ollie的必胜态
............
必胜态是对称的!!!
如果"我方"首先给出了一个在N不断除18后的得到不足18的
数M,"我方"就可以取得胜利,然而双方都很聪明,所以这样胜负就决定于N了,如果N不断除
18后的得到不足18的数M,如果1<M<=9则先手胜利,即Stan wins.如果9<M<=18
则后手胜利.

#include<cstdio>
int main()
{
    double n;
    while(scanf("%lfd", &n)!=EOF)
    {
        while(n>18) n/=18;
        if(n<=9) printf("Stan wins.
");
        else printf("Ollie wins.
");
    }
    return 0;
}
View Code

(2)威佐夫博奕(Wythoff Game):有两堆各若干个物品,两个人轮流从某一堆或同
时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。


题目:http://acm.hdu.edu.cn/showproblem.php?pid=1527(威佐夫博弈的题目还真是难找啊!, 模板练习!)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
int main()
{
    int a, b, temp, k;
    while(scanf("%d%d", &a, &b)!=EOF)
    {
        if(a<b) std::swap(a, b);
        int k = a-b;
        a = (int)(k*(1+sqrt(5))/2.0);
        if(a==b) printf("0
");
        else printf("1
");
    }
    return 0;
}
View Code

题目:http://acm.nyist.net/JudgeOnline/problem.php?pid=837

 题意:求前 n 组必败局势。 (提示:直接暴力!)。

#include<cstdio>
#include<cstring>
const int MAXN = 100000+10;

int a[MAXN], b[MAXN], v[MAXN];

void solve(int n)
{
    int i, j;
    memset(v, 0, sizeof(v));
    v[0] = 1;
    for(i=1; i<=n; i++)
    {
        for(j=1; j<MAXN; j++)
        {
            if(!v[j])
            {
                v[j] = 1;
                a[i] = j;
                b[i] = i+j;
                v[i+j] = 1;
                break;
            }
        }
    }
}

int main()
{    
    int n;
    while(scanf("%d", &n)!=EOF)
    {
        int i;
        solve(n);
        for(i=0; i<=n; i++)
        printf("(%d,%d)", a[i], b[i]);
        printf("
");
    }
    return 0;
}
View Code

(3)尼姆博奕(Nimm Game):有三堆各若干个物品,两个人轮流从某一堆取任意多的
物品,规定每次至少取一个,多者不限,最后取光者得胜。

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1849

裸裸的模板题。

#include<cstdio>
int main()
{
    int n;
    while(scanf("%d", &n)!=EOF&&n)
    {
        int ans = 0;
        int a;
        scanf("%d", &a);
        ans = a;
        for(int i=1; i<n; i++)
        {
            scanf("%d", &a);
            ans^=a;
        }
        if(ans==0) printf("Grass Win!
");
        else printf("Rabbit Win!
");
    }
    return 0;
}
View Code

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1907

简单分析:  这个题分两种情况。 各种糖果数量全部为1。 那么, 每次减少一堆, 由n的奇偶性便可判断出结果。 另一种情况便是不全为1。 那么直接用尼姆博弈的计算方法就可以得出结果。

#include<cstdio>
int main()
{
    int a[60];
    int  T, n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        int temp = 0;
        int ans;
        for(int i=0; i<n; i++)
        {
            scanf("%d", &a[i]);
            if(i==0) ans = a[i];
            else
                ans = ans^a[i];
            if(a[i]>1) temp = 1;
        }
        if(temp==0)//全是孤单堆。 
        {
            if(n%2) 
                printf("Brother
");
            else
                printf("John
");
                continue;
        }
         if(ans==0)
            printf("Brother
");
        else 
            printf("John
");
    }    
    return 0;
}
View Code

题目: http://acm.hdu.edu.cn/showproblem.php?pid=2509

和上题完全一样的题目。

#include<cstdio>
int main()
{
    int a[105];
    int n;
    while(scanf("%d", &n)!=EOF)
    {
        int temp = 0;
        int ans;
        for(int i=0; i<n; i++)
        {
            scanf("%d", &a[i]);
            if(i==0) ans = a[i];
            else
                ans=ans^a[i];
            if(a[i]>1) temp = 1;
        }
        if(temp==0)//全是孤单堆。
        {
            if(n%2) printf("No
");
            else printf("Yes
");
        } 
        else if(ans==0)
            printf("No
");
        else 
            printf("Yes
");
    }
    return 0;
}
View Code

 题目:http://acm.hdu.edu.cn/showproblem.php?pid=1850(此题需要理解尼姆博弈的证明过程) 

/*本题考查对尼姆博弈的理解, 简单的记结论对此题已无卵用!
                                    
解题方法:
(1)如若给出 的是必败状态:a1^a2^......^an=0,则先手不会有任何可能获得胜利;
(2)若给出的是必胜状态:a1^a2^.......^an=k,(其中k不为零),那么我们的目的是
要把必胜状态转化为必败状态从 而使得先手胜利。若a1^a2^...^an!=0,一定存在某个
合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。若a1^a2^...^an=ans,
则一定存在某个ai它的二进制 表示在ans的最高位上是1(否则asn的最高位那个1是
怎么得到的)。这时ai^ans<ai(最高位的1没啦)一定成立。则我们可以将ai改变成
ai'=ai^k,此时a1^a2^...^ai'^...^an=a1^a2^...^an^k=0。

*/
#include<cstdio>

int main()
{
    int n, a[110];
    while(scanf("%d", &n)!=EOF&&n)
    {
        int tot=0;
        int ans = 0;
        for(int i=0; i<n; i++)
        {
            scanf("%d", &a[i]);
            ans^=a[i];
        }
        for(int i=0; i<n; i++)
        {
            if(a[i]>(ans^a[i])) tot++;
        }
        printf("%d
", tot);
    }
    return 0;
}


 
View Code

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1851(巴什博弈+尼姆博弈)

/*
先对各堆石子进行巴什博弈,然后每堆石子能能取的限制就 
 只是大于等于 1 啦。(因为此时的最大能取限度大于该堆石子数)   
*/ 
#include<cstdio>
int main()
{
    int T;
    int n,ans, m, l;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        ans = 0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d", &m, &l);
            ans=ans^(m%(l+1));
        }
        if(ans==0) printf("Yes
");
        else printf("No
");
    }
    return 0;
}
View Code

 其他类型博弈:(这部分博弈可能和上面的三种博弈有相似之处但是因为有其独特的脑洞, 所以单独写在下面并加以解释)

 题目:http://acm.hdu.edu.cn/showproblem.php?pid=1847(巴什博弈变形)

 题目:http://acm.hdu.edu.cn/showproblem.php?pid=2147(简单找规律)

/*
   画几个框框就一目了然啦! 
*/ 
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m))
    {
        if(n==0&&m==0) break;
        if(n%2&&m%2) printf("What a pity!
");
        else printf("Wonderful!
");
    }
    return 0;
}
View Code

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

/*
    这个博弈也是可贪心的。每一步就要尽量的为自己争取
    最大的利益或者为对方带来最大的损失。
    最后的胜利是属于谁的稳定走位多的。 
*/

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
int a[16];
int main()
{
    int T;
    bool now;//记录现在是谁走,1表示Alice ,2表示 Bob;
    int Alice,Bob;//分别是两人的保留步数
    scanf("%d",&T);
    for(int Kase=1; Kase<=T; Kase++)
    {
        printf("Case #%d: ",Kase);
        for(int i=1;i<=15;i++)
          scanf("%d",&a[i]);
        Alice=Bob=0;
        now=0;//Alice先走
        if(a[15]%2==0)//先抢第15种
        {
            Alice+=a[15]/2;//加一样的可以不加的,这里便于理解
            Bob+=a[15]/2;
        }
        else
        {
            Alice+=a[15]/2+1;
            Bob+=a[15]/2;
            now=1;//Bob先走了
        }
        int tempa=a[5]+a[6];//ALice抢5,6
        int tempb=a[3]+a[4];//BOb抢3,4
        if(tempa==tempb)
        {
            Alice+=tempa;
            Bob+=tempb;
        }
        else if(tempa<tempb)//Alice抢完5,6会来抢3,4的
        {
            Alice+=tempa;
            Bob+=tempa;
            tempb-=tempa;
            if(tempb%2==0)
            {
                Bob+=tempb/2;
            }
            else
            {
                if(now==0)
                {
                    now=1;
                    Bob+=tempb/2;
                }
                else
                {
                    now=0;
                    Bob+=tempb/2+1;
                }
            }
        }
        else
        {
            Alice+=tempb;
            Bob+=tempb;
            tempa-=tempb;
            if(tempa%2==0)
            {
                Alice+=tempa/2;
            }
            else
            {
                if(now==0)
                {
                    now=1;
                    Alice+=tempa/2+1;
                }
                else
                {
                    now=0;
                    Alice+=tempa/2;
                }
            }
        }

        int temp=a[11]+a[12]+a[13]+a[14];//两人一起抢了
        if(temp%2==0)//刚好平分
        {
            Alice+=0;
            Bob+=0;
        }
        else
        {
           now = !now;
        }
        tempa=a[7]+a[8];
        tempb=a[9]+a[10];
        if(tempa==tempb)
        {
            Alice+=0;
            Bob+=0;
        }
        else if(tempa<tempb)//Alice抢完5,6会来抢3,4的
        {
            Alice+=0;
            Bob+=0;
            tempb-=tempa;
            if(tempb%2==0)
            {
                Bob+=tempb/2;//会留下一个
            }
            else
            {
                if(now==0)
                {
                    now=1;
                    Bob+=tempb/2+1;
                }
                else
                {
                    now=0;
                    Bob+=tempb/2;
                }
            }
        }
        else
        {
            tempa-=tempb;
            if(tempa%2==0)
            {
                Alice+=tempa/2;
            }
            else
            {
                if(now==0)
                {
                    now=1;
                    Alice+=tempa/2;
                }
                else
                {
                    now=0;
                    Alice+=tempa/2+1;
                }
            }
        }
        Alice+=2*a[1];
        Bob+=2*a[2];
        if(now==0)
        {
            if(Bob>=Alice)printf("Bob
");
            else printf("Alice
");
        }
        else
        {
            if(Alice>=Bob)printf("Alice
");
            else printf("Bob
");
        }
    }
    return 0;
}
Talk is cheap

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

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int MAXN = 1010;
bool sg[MAXN][MAXN];
bool used[MAXN][MAXN];
int n,m;
bool check(int x,int y)
{
    if(x > 0 && sg[x-1][y])return true;
    if(x < n-1 && sg[x+1][y])return true;
    if(y > 0 && sg[x][y-1])return true;
    if(y < m-1 && sg[x][y+1])return true;
    if(x > 0 && y > 0 && sg[x-1][y-1])return true;
    if(x > 0 && y < m-1 && sg[x-1][y+1])return true;
    if(x < n-1 && y > 0 && sg[x+1][y-1])return true;
    if(x < n-1 && y < m-1 && sg[x+1][y+1])return true;
    return false;
}
int dfs(int x,int y)
{
    queue<pair<int,int> >q;
    q.push(make_pair(x,y));
    int cnt = 0;
    used[x][y] = true;
    while(!q.empty())
    {
        pair<int,int> tmp = q.front();
        q.pop();
        int nx = tmp.first;
        int ny = tmp.second;
        if(check(nx,ny))
        {
            cnt++;
            continue;
        }
        for(int c=-1; c<=1; c++)
        for(int r=-1; r<=1; r++)
        {
            if(c||r)
            {
                int tx = nx + c;
                int ty = ny + r;
                if(tx < 0 || tx >= n || ty < 0|| ty >= m)continue;
                if(used[tx][ty])continue;
                if(sg[tx][ty])continue;
                q.push(make_pair(tx,ty));
                used[tx][ty] = true;
            }
        }
    }
    return cnt;
}

int main()
{
    int T;
    scanf("%d",&T);
    int iCase = 0;
    while(T--)
    {
        iCase ++;
        int k;
        scanf("%d%d%d",&n,&m,&k);
        memset(sg,false,sizeof(sg));
        int x,y;
        while(k--)
        {
            scanf("%d%d",&x,&y);
            sg[x][y] = true;
        }
        memset(used,false,sizeof(used));
        int ans = 0;
        for(int i = 0;i < n;i++)
            for(int j = 0 ;j < m;j++)
                if(!sg[i][j] && !used[i][j] && !check(i,j))
                {
                    int tmp = dfs(i,j);
                    ans ^=  (tmp%2+1);
                }
        for(int i = 0;i < n;i++)
            for(int j = 0;j < m;j++)
                if(!sg[i][j] && !used[i][j] && check(i,j))
                    ans ^= 1;
        if(ans == 0)printf("Case #%d: Fanglaoshi
",iCase);
        else printf("Case #%d: Xiemao
",iCase);
    }
    
    return 0;
}
Talk is cheap

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

/*这个题的规律会随天数变化,按天进行逐步分析, 
就会惊奇的发现, (天数+月份)是偶数时, 胜利,反之败。
但是有两个例外: 9月30日和11月30日。
*/
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

int main()
{
    int T;
    int y, m, d;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &y, &m, &d);
        if((d+m)%2==0||(m==9&&d==30)||(m==11&&d==30))
        printf("YES
");
        else printf("NO
");
    }
    return 0;
}
Talk is cheap

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

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

const int MAXN = 1000000;
bool sg[MAXN];
int Get_length(int n)
{
    if(n/100000) return 6;
    if(n/10000) return 5;
    if(n/1000) return 4;
    if(n/100) return 3;
    if(n/10) return 2;
    return 1;
}

void Get_sg(int n)
{
    int len = Get_length(n);
    int i;
    for(i=len; i>=1; i--)
    {
        int m = n;
        int base = 1;
        for(int j=1; j<i; j++) base*=10;
        int tmp=(m%(base*10))/base;//取最高位。 
        for(int j=tmp; j<9; j++)
        {
            m+=base;
            sg[m] = 1;            //先手必胜局势。 
        }
    }
    if(len!=6) //长度小于6时,在后面加个0  
    {          //然后用任意数补齐。 
        int m=n;
        int base = 1;
        for(int i=len; i<6; i++)
        {
            m*=10;
            for(int b=0; b<base; b++)
            sg[m+b] = 1;
            base*=10;
        }
    }
}

void fun()
{
    memset(sg, 0, sizeof(sg));
    sg[0] = 1;         //为 000000时, 先手必胜。 
    for(int i=1; i<MAXN; i++)
    {
        if(!sg[i]) Get_sg(i);
    }
}

int main()
{
    char str[8];
    int n;
    fun();
    while(scanf("%s", &str)!=EOF)
    {
        if(str[0] == '0')
        {
            printf("Yes
");
            continue;
        }
        int len = strlen(str);
        n = 0;
        for(int i=0; i<len; i++)
        {
            n*=10;
            n+=str[i]-'0';
        }
        if(sg[n]) printf("Yes
");
        else printf("No
");
    }
    return 0;
}
View Code

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

/*
    此题直接简单分析一下就行啦。 我们假定 a > b 
    《1》 a == b 先手必胜。
    《2》如果 a%b==0, 先手必胜。
    《3》如果  b<a<2*b 。 先手只有一个选择。 转化成 b, a-b。 这样一直下去,,,
         判断到底鹿死谁手。 
    《4》如果 a > 2*b 那么玩家一定知道 a%b, b是必败局势
         还是必胜局势。 如果是必败局势, 先手将 a,b 变成
         b,a%b 那么先手肯定能赢。 先手将a,b转化成 a%b+b, b
         那么对手只有将俩个数变成 b, a%b。 仍然是先手必胜。 
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

int main()
{
    int a, b;
    while(scanf("%d%d", &a, &b)==2)
    {
        if(a==0||b==0) break;
        if(a<b) swap(a, b);
        bool ans = true;
        while(1)
        {
            if(a==b||a/b>=2) break;
            a=a-b;
            swap(a, b);
            ans = !ans;
        }
        if(ans) printf("Stan wins
");
        else printf("Ollie wins
");
    }
    return 0;
}
Talk is cheap

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

//提示: 用1*2的瓷砖覆盖地图 。
//知识拓展: 参见《组合数学》之棋盘的完美覆盖。 
#include<cstring>
#include<cstdio>
#include<alorithm>
#include<iostream>
using namespace std;

int main()
{
    int n;
    while(scanf("%d", &n)!=EOF, n)
    {
        if(n%2==0) printf("8600
");
        else printf("ailyanlu
");
    }
    return 0;
}
Talk is cheap

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

/*
    很显然要用 SG函数解决此题。 但是如何求SG 的函数值呢?
    假设 箱子里最多放 S 个球。 现在有 a 个球。 只需求出一个
    最大的 t 。 
    t + t*t < S 。 当然: a == t 时。是一个临界条件。 
一 。当 a > t 时。 肯定能一次达到装满的状态。 所以是必胜态。
    返回的sg值为 S - a。 我们知道最终的状态是(S,S)。 而
    (S, S-1)只能到达(S,S),所以这一点的sg值为1。以此类推
    为 2, 3, ,,,所以到(S, a)的时候, SG值为 S - a。
二。当 a == t 时, 到这个状态的人, 一次肯定无法装满盒子。
    而下一个人一定可以一次装满盒子。所以这是个奇异局势,必败!
    SG函数值 为 0 。 
三。当 a < t 时。 可以把 t 当作 S , 直接递归调用。 但是为什么
    可以这么做呢? 因为我们如果想赢, 只需将对方陷入奇异局势即可
    即(t, S)的局面。 即:只需将 盒子装到 t 个即可。也就是达到
    (a, t)的局面。
由于第二部分是第三部分的一部分。 所以也能递归调用。 从而递归求出SG
函数值。  
*/
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

int Get_SG(int c, int s)
{
    int t = sqrt(s);
    while(t*t+t>=s) t--;
    if(c>t) return s-c;
    else return Get_SG(c, t);
}

int main()
{
    int c, s, ans, n;
    int Kase = 0;
    while(scanf("%d", &n), n)
    {
        printf("Case %d:
", ++Kase);
        ans = 0;
        while(n--)
        {
            scanf("%d%d", &s, &c);
            ans^=Get_SG(c, s);
        }
        if(ans) printf("Yes
");
        else printf("No
");
    }
    return 0;
}
Talk is cheap

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

//简单博弈, 模拟分析一下就会发现。 如果
//两个子左右挨着, 对先手不利, 否则对先手有利。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

 int main()
 {
     int n, m;
     while(scanf("%d%d", &n, &m)!=EOF)
     {
         int ans = 0;
         int x, y;
         for(int i=1; i<=n; i++)
         {
             scanf("%d%d", &x, &y);
             int k = abs(y-x);
             ans^=k-1;
         }
         if(!ans) printf("BAD LUCK!
");
         else printf("I WIN!
");
     }
     return 0;
 }
Talk is cheap

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

//赤裸裸的巴什博弈。 不忍直视。 
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        if(n%(m+1)==0) printf("second
");
        else printf("first
"); 
    }
    return 0;
}
View Code

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

暴力打表,找规律!

/*
    此题关键是求SG函数值,求SG函数值的思路是
    直接枚举。然而看一下面的程序就可以知道,时间复杂度
    太大。可以打出 SG函数值表, 进行观察一下。
     会有惊奇的发现。 
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int MAXN = 1010;
int sg[MAXN];
bool vis[MAXN];

int mex(int v)
{
    if(sg[v]!=-1) return sg[v];
    memset(vis, false, sizeof(vis));
    int tmp;
    tmp = mex(0);
    vis[tmp] = true;
    for(int i=1; i<v; i++)
    {
        tmp = mex(v-i);
        vis[tmp] = true;
        tmp=mex(i)^mex(v-i);
        vis[tmp] = true;
    }
    for(int i=0; i<MAXN; i++)
    if(vis[i]==false)
    {
        sg[v] = i;
        break;
    }
    return sg[v];
}

int main()
{
    memset(sg, -1, sizeof(sg));
    sg[0] = 0;
    int T, n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        int ans = 0;
        int v;
        while(n--)
        {
            scanf("%d", &v);
            printf("%5d", mex(v));
            //ans^=mex(v);
        }
        if(ans==0) printf("Bob
");
        else printf("Alice
");
    }
    return 0;
}
Talk is cheap,

找出规律,怒切此题!

//发现规律是:  如果 i%4==0 则: sg[i] = i-1;
//             如果 i%4==3 则: sg[i] = i+1;
//             其余  sg[i] = i;

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int mex(int v)
{
    if(v==0) return 0;
    if(v%4==0) return v-1;
    if(v%4==3) return v+1;
    return v;
}

int main()
{
    int T, n;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        int ans = 0;
        int v;
        while(n--)
        {
            scanf("%d", &v);
            ans^=mex(v);
        }
        if(ans==0) printf("Bob
");
        else printf("Alice
");
    }
    return 0;
}
show me the code。

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

//SG函数的应用。 把矩阵转化成字符串,穷举SG值 
#include<stdio.h>
#include<string.h>
#include<string>
#include<map>
using namespace std;
int n,m;

map<string,int>vis_str;
map<string,int>sg;
string s;
void add(int a[][50])//把矩阵转化为一个字符串
{
    int i,j;
    s="";
    for(i=0;i<n;i++)
        for(j=0;j<m;j++)
          s+=a[i][j]+'0';
}

int mex(int a[][50])
{
    int i,j,k;
    int vis_sg[100];
    memset(vis_sg,0,sizeof(vis_sg));
    add(a);
    vis_str[s]=1;
    for(i=1;i<n;i++)
        for(j=1;j<m;j++)
        {
            if(a[i][j]==0&&a[i-1][j]==0&&a[i][j-1]==0&&a[i-1][j-1]==0)
            {
            a[i][j]=a[i-1][j]=a[i][j-1]=a[i-1][j-1]=1;
                add(a);
                if(vis_str[s])
                    k=sg[s];
                else
                    k=mex(a);
                vis_sg[k]=1;
            a[i][j]=a[i-1][j]=a[i][j-1]=a[i-1][j-1]=0;
            }
        }
        for(i=0;;i++)
        {
            if(!vis_sg[i]) {  add(a); sg[s]=i; return i;}
        }
}
int main()
{
    int i,j,a[50][50],t,ans;
    while(scanf("%d",&t)!=EOF)
     {
          ans=0;
         while(t--)
         {
            scanf("%d %d",&n,&m);
            for(i=0;i<n;i++)
            for(j=0;j<m;j++)
                scanf("%1d",&a[i][j]);
            ans=ans^mex(a);
            sg.clear();
            vis_str.clear();
        }
             if(ans) puts("Yes");
             else  puts("No");
     }
    return 0;
}
Talk is cheap

题目:http://www.cnblogs.com/kuangbin/archive/2011/08/30/2159465.html

/*
这个问题的解决方法似乎不是那么明显。 所以我们先手算
枚举几个必败点。 2 , 3, 5, 8 ,,,发现问题了吧。
这个数列好熟悉啊! 不错, 就是斐波那契数列! 就是这
般的巧合。就是如此的迷人! 但是为什么呢? 例如 n = 12时
只要谁能使石子剩下8个, 并且此次的取子没超过3就能获胜。因此
可以把12看作8+4, 把8看作一个站,等价于对4进行“气喘操作”。
如果 n = 13=8+5 . 5 本来就是必败点,得出13也是必败点。 
*/ 

/*注 “Zeckendorf定理”(齐肯多夫定理):
    任何正整数可以表示为若干个不连续的Fibonacci数之和
*/ 
#include<cstdio>
int main()
{
    int fib[45];
    int n, flag;
    fib[0] = 2; fib[1] = 3;
    for(int i=2; i<45; i++)
    fib[i] = fib[i-1]+fib[i-2];
    while(scanf("%d", &n)!=EOF&&n)
    {
        flag=0;
        for(int i=0; i<45; i++)
        {
            if(fib[i]==n)
            {
                flag = 1;
                break;
            }
        } 
        if(flag) printf("Second win
");
        else printf("First win
");
    }
    return 0;
} 
View Code

简单组合博弈模型详解: http://www.cnblogs.com/acm1314/p/4647034.html

原文地址:https://www.cnblogs.com/acm1314/p/4643256.html