先给出四大博弈的定义和基本做法。

1、巴什博弈:只有一堆共n个石子,两人轮流取,最少取一个,最多取m个,最先取光石子的人赢。
做法:如果n%(m+1)!=0则先手必胜,否则后手必胜。

2、威佐夫博弈:有两堆各x,y个石子,两人轮流从某一堆或同时从两堆中取同样多的石子,规定每次至少取一个,多者不限,最先取光石子的人赢。
做法:若两堆物品的初始值为(x,y),且x< y,记w=(int)((sqrt(5.0)+1.0)/2.0∗(y-x));如果w=x则先手必输,w!=x则先手必胜。

3、尼姆博弈:有n堆石子(可假设分别为a[1]~a[n]个石子),两人轮流从某一堆取任意多的石子,规定每次至少取一个,多者不限,最先取光石子的人赢。
做法:将所有的石子堆异或,设为ans,ans!=0则先手必胜,否则后手必胜。

4、反尼姆博弈:尼姆博弈是最先取光石子的人赢,而反尼姆博弈是最先取光石子的人输。
做法:将所有的石子堆异或,设为ans,反尼姆博弈中先手必胜有两种可能:
①ans!=0且存在a[i]>1
②ans=0且所有a[i]=1(因为每个石子堆只有一个石子且可以证明石子堆个数必为偶数,所以显然总是后手最先取光石子,也就是后手必败,先手必胜)

5、其他概念:先手必胜为N-position,后手必胜为P-position。
* 必败点(P态):前一个选手(Previous player)将取胜的位置称为必败点。
* 必胜点(N态) :下一个选手(Next player)将取胜的位置称为必胜点。
* 现在关于P,N的求解有三个规则
(1):最终态都是P(游戏规则是:最后不能进行操作的人输)
(2):按照游戏规则,到达当前态的前态都是N的话,则当前态是P
(3):按照游戏规则,到达当前态的前态至少有一个P的话,则当前态是N

hdu 1846 Brave Game

巴什博弈模板题。
如果n%(m+1)不为0可以先选n%(m+1),之后每次通过对方选的个数X选m+1-X个保证取到最后一个,此时先手必胜,相反后手可以控制这个过程来使得后手必胜。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n,m,t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        if(n%(m+1)==0)printf("second\n");
        else printf("first\n");
    }
    return 0;
}

poj 1067 取石子游戏

威佐夫博弈模板题。

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int x,y,w;
    ios::sync_with_stdio(false);
    while(cin>>x>>y)
    {
        w=(int)((sqrt(5.0)+1.0)/2.0*fabs(y-x));
        if(w==(min(x,y)))printf("0\n");
        else printf("1\n");
    }
    return 0;
}

poj 2234 Matches Game

尼姆博弈模板题。

#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int m,x,ans;
    while(cin>>m)
    {
        ans=0;
        while(m--)
        {cin>>x;ans=ans^x;}
        if(ans==0)printf("No\n");//所有石子异或为0,先手必输
        else printf("Yes\n");
    }
    return 0;
}

hdu 2509 Be the Winner

反尼姆博弈模板题。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int i,n,ans,flag,a[101];
    while(cin>>n)
    {
        ans=flag=0;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];ans=ans^a[i];
            if(a[i]>1)flag=1;
        }
        if(ans==0&&flag==0||ans!=0&&flag==1)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

poj 2975 Nim

尼姆博弈。
给定nim游戏的一种局面,你是A,对手是B,问你现在这一步有多少种取法使得你进入必胜态。
先求出原状态下所有石子的异或,设为tmp。判断tmp,如果tmp=0,你开始取石子,相当于你是先手,必输,即获胜的方法数为0,直接输出0即可。
如果tmp!=0,那么在你取了之后,就是B取,可以看成你取了石子(必须至少取一个)改变状态后,才正式开始游戏,这时可以把B看作先手,B开始在你取了石子之后的新状态下开始取。要使在你取了石子后的新状态下,作为先手的B必输,也就是要上一步你取了石子之后,所有石子异或为0,此时B必输,A必赢。
遍历a[i],上一步你只能选择一堆石子a[i]取走任意个石子,除a[i]以外的所有石子异或 tmp^a[i] 不变。要在a[i]中取走任意个石子后B必输,也就是要保证上一步你取了石子之后所有石子异或为0,也就是要在a[i]中取任意个石子后剩下的数量为tmp^a[i],那么只要 tmp^a[i]<a[i] 即可满足条件。

#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int n,i,tmp,cnt,a[1001];
    ios::sync_with_stdio(false);
    while(cin>>n&&n)
    {
        tmp=cnt=0;
        for(i=1;i<=n;i++)
        {cin>>a[i];tmp=tmp^a[i];}
        if(tmp==0){printf("0\n");continue;}//tmp=0说明在你取石子之前所有石子异或为0,此状态先手必输,这时你是先手,必输
        for(i=1;i<=n;i++)
        {if((tmp^a[i])<a[i])cnt++;}//你已经取了石子,并且保证了(tmp^a[i])<a[i],则你取石子后先手的B必输
        //if(tmp^a[i]<a[i])是错的,没在tmp^a[i]外写括号则会先计算<符号再计算^符号异或优先级低于关系运算符
        printf("%d\n",cnt);
    }
    return 0;
}

poj 2484 A Funny Game

博弈,从进入对称状态入手。
当n<=2时,显然A必胜;当n>=3时,A先手取走一个或两个硬币后,由于硬币组成的环被截断,把环拉直,相当于环被切断后变成了"一条绳子",把"绳子"看成一段序列,那么B则根据这段序列长的奇偶性取出序列最中间的一个或两个硬币,从而把"这条绳子"再切成"两条对称的绳子",这时进入了对称状态,A开始取,由于被切断后的"两条绳子"对称,那么以后的每一步,B只要模仿A取硬币的方式,A取几个硬币,B就可以在对称位置上取几个硬币,这样就可以保证B总会取到最后一个硬币,B必胜。实际上,进入对称状态,就相当于有两堆一样多的石子,A先手开始取,根据尼姆博弈,两堆石子异或为0(因为两堆石子个数一样),所以A必败。

#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int n;
    while(cin>>n&&n)
    {
        if(n<=2)printf("Alice\n");
        else printf("Bob\n");
    }
    return 0;
}

参考资料:
尼姆博弈 https://blog.csdn.net/k_koris/article/details/81743806