序 : 下面的例题只给了题源,没给题目链接,但是都可以根据对应的题号找到题目来提交代码,例如HDU 2897就是hdoj的2897题。然后题意大概都是我YY之后的,也许有些描述错误,然后某些解题思路来自网上大神的博客,如果有觉得侵犯了权利请联系我(ps,本来就是写给自己看的,巨丑,然后每个地方好放,放到博客怎么也会给人看到的,请多多包涵)

博弈真的是智力题啊。。。

1, HDU 2897
题意:一堆n个石子,每次最多取q个,最少取p个,切最后不少于p个时必须一次性取完。谁后取完者输。

解题思路: 那么就是谁先取到剩p个时候就赢了,少于p个的话一定输,因为必须取完,如果谁当前遇到0个,赢,大于p小于p+q个是赢,因为先手总是可以想办法给后手留下p个,让他不得不拿。推理发现n个和n-(p+q)输赢一样,所以首先可对n%=(p+q)。

//HDU 2987
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, p, q;
    while(scanf("%d%d%d", &n, &p, &q) != EOF)
    {
        if(p > q) swap(p, q); //应该没有和题意不合的数据,这句话多余
        n %= (p + q);
        if(n <= p && n != 0) puts("LOST");
        else puts("WIN");
    }
    return 0;
}

2, HDU 2516
题意:1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出”Second win”.先取者胜输出”First win”。
解题思路: Fib尼姆博弈,这个问题我是真的说不清楚,然后对于这个博弈结论的证明感觉也是比较迷糊的,反正记住这个典型模型就行了,具体证明可以看世界冠军cxlove神的博客:http://blog.csdn.net/acm_cxlove/article/details/7835016

//HDU 2516
#include <bits/stdc++.h>
using namespace std;
int fib[50];
int main()
{
    fib[0] = 1;
    fib[1] = 2;//这里和普通fib不一样,自己推一下就明白了
    for(int i = 2; i < 45; i++) fib[i] = fib[i-1] + fib[i-2]; //预处理fib
    int n;
    while(scanf("%d", &n)!=EOF)
    {
        if(n == 0) break;
        int i = 0;
        for(i = 0; i < 45; i++){
            if(fib[i] == n) break;
        }
        if(i < 45) puts("Second win");
        else puts("First win");
    }
    return 0;
}

3,HDU 2147
题意:一个n*m的表格,起始位置为右上角,目标位置为左下角,甲先开始走,走的规则是可以向左,向下或者向左下(对顶的)走一格。谁先走到目标位置谁就胜利。在甲乙都采用最佳策略的时候,先走者能否获胜。
下面的节选自这位大神博客的一段话

解题思路: 巴什博奕的模型,关键为画出PN图,那什么是PN图呢?等等先介绍一下巴什博奕: 只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果
n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。那么这个时候只要n%(m+1)!=0,先取者一定获胜。
    这个游戏还可以有一种变相的玩法:两个人轮流报数,每次至少报一个,最多报十个,谁能报到100者胜。
       分析此类问题主要放法是:P/N分析:
       P点:即必败点,某玩家位于此点,只要对方无失误,则必败;
       N点:即必胜点,某玩家位于此点,只要自己无失误,则必胜。
三个定理:
       一、所有终结点都是必败点P(上游戏中,轮到谁拿牌,还剩0张牌的时候,此人就输了,因为无牌可取);
       二、所有一步能走到必败点P的就是N点;
       三、通过一步操作只能到N点的就是P点;
巴什博弈的一个最重要的特征就是只有一堆。然后就在其中改,要么在范围内不规定个数,要么就规定只能取几个,再要么就倒过来,毕竟是最简单的博弈,代码相对而言较短额~

然后根据PN规则我们可以分析出这个题的PN图如下:
例如,n = 8, m = 9时
NNNNNNNNN
PNPNPNPNP
NNNNNNNNN
PNPNPNPNP
NNNNNNNNN
PNPNPNPNP
NNNNNNNNN
PNPNPNPNP
初始点(1,1)为N所以输出Wonderful!
从这里例子就可以很清楚得看出当n和m都为奇数时,初始点(1,1)才会是P。
所以这个问题就判断了n和m是否同奇。

//HDU 2147
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        if(n == 0 && m == 0) break;
        if(n % 2 == 1 && m % 2 == 1){
            printf("What a pity!\n");
        }
        else{
            printf("Wonderful!\n");
        }
    }
    return 0;
}

4, HDU 3951

题意:n个硬币放成一圈,每次最多取连续k个,不能不取。最后取完者胜。
解法:一个非常容易想到的博弈了。n个硬币,不论n是奇数偶数,后手总能够在第一轮把它变成对称两部分的状态,对称状态下后手肯定赢。那么先手能赢只能是k>=n或者每次只能取一个且n是奇数。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int T, n, k, ks = 0;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &k);
        if(k >= n || (k == 1 && n & 1)) printf("Case %d: first\n", ++ks);
        else printf("Case %d: second\n", ++ks);
    }
    return 0;
}

5,HDU 3389 变相的Nim博弈
题意:给出n个盒子,编号1–n,在满足b>a && (a+b)%2==1 && (a+b)%3==0 && b不为空,的情况下从b中取出任意多个石子放入a中,不能操作者输,求先手输赢?

解法:首先,要满足(a+b)%3==0 ,既为3的倍数,又(a+b)%2==1,既为奇数,所以(a+b)为 3 , 6 , 9, 15 , 21…依次+6,发现周期为6。在一个周期 0 - - 6中。只有(1,2)(3 ,6 )(4,5)满足条件,那么就可以从 2 6 5中取出其中石子,变为nim游戏。所以只要求 i%6 == 0 || 1 || 5,的值异或就行。
这个题最脑洞是找到周期转化成nim求解。
代码可以这样写

#include <bits/stdc++.h>
using namespace std;
int main(){
    int T, n, ks = 0;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        int SG = 0;
        for(int i = 1; i <= n; i++){
            int x;
            scanf("%d", &x);
            if(i%6 == 0 || i%6 == 2 || i%6 == 5){
                SG ^= x;
            }
        }
        if(SG == 0){
            printf("Case %d: Bob\n", ++ks);
        }
        else{
            printf("Case %d: Alice\n", ++ks);
        }
    }
    return 0;
}

6, HDU 1517
题意: 你和一个人玩游戏,给你一个数字n,每次操作可以从2~9中任选一个数字,并把它与p相乘,(游戏开始时p=1)两人轮流操作,当一个人操作完后p>=n,这个人就是胜者。
解法:这就非常的神了,上面已经说了PN图了,并且假设我们也会画PN图了,但是这个题是画不出来上面那个题那种图 然后去找规律的(其实也是有规律的,但是我不会),我们只能通过计算PN值来判断。由于每次都是从p=1开始的,所以只要判断每个游戏中1为必败点还是必胜点即可。(以下各式 / 均为取上整)依照上面所提到的算法(就是PN图的规则,下面一会我再重点把这个算法摆出来),将终结位置,即[n,无穷]标记为必败点;然后将所有一步能到达此必败段的点标记为必胜点,即[n/9,n-1]为必胜点;然后将只能到达必胜点的点标记为必败点,即[n/9/2,n/9-1]为必败点;重复上面2个步骤,直至可以确定1是必胜点还是必败点。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, x;
    while(scanf("%d", &n) != EOF)
    {
        for(x = 0; n > 1; x++){
            if(x&1) n = ceil((n*1.0/2));
            else n = ceil((n*1.0/9));
        }
        puts(x&1?"Stan wins.":"Ollie wins.");
    }
    return 0;
}

求PN图算法:

算法实现:
步骤1:将所有终结位置标记为必败点(P点);(终结位置指的是不能将游戏进行下去的位置)
步骤2:将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点)
步骤3:如果从某个点开始的所有一步操作都只能进入必胜点(N点) ,则将该点标记为必败点(P点) ;
步骤4:如果在步骤3未能找到新的必败(P点),则算法终止;否则,返回到步骤2

7, BNUOJ 4353
只是觉得这个题目的分析非常有意思,看到了就记录下来了。
题意:给出一个m*n的矩阵,每次可以选择一个点,把其右上角的格子全部删去,谁删掉了最左下角的格子的话为输,求先手输赢。
解法:
首先看 1 * n 的格子,出了 1 * 1 的其他的都先手赢,因为先手可以一次取完只给队友留校左下一个。2 * n 的格子,先手赢。看 2 * 2 的,先手必定先取右上一个,那么剩下的后手不能一次取完,后手取完先手取剩下的给后手留给左下的。2 * 3 的先手赢,前面知 2 * 2 的先手赢,那么谁能取到面对 2 * 2 的则必胜,先手必先取最右下一个,那么不论后手怎么取都输。2 * n 的,都是先手赢,总可以推到前面的,而前面无论哪个都是先手赢。所以。。
3 * n 的,先手赢,首先先手一定不能一次变成 2 * n 的,因为后手面对2 * n 的则先手必输,那么先手可以取到 2 * n 多一个。那么他必胜。推理发现除了 1 * 1 的所有情况都是先手必胜。所以太简单就不给代码了。

8,Updating。。。

后记:完成得比较匆忙,有错误希望大家指出,谢谢QAQ。