简单的博弈

这里转载一下别人的博客:http://blog.csdn.net/ac_gibson/article/details/41624623
另一个大佬的https://www.cnblogs.com/kuangbin/archive/2011/08/28/2156426.html
链接:https://www.nowcoder.net/acm/contest/75/D
来源:牛客网

巴什博奕:
只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个,最后取光者得胜。
那么当n和m分别是什么关系的时候,能保证先手必胜?
经过推导我们可以发现当n=m+1时,由于一次最多取m个,无论先手取多少个,后手都能一次拿走剩余物品。
继续来推导,我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,1 ≤ s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。这就是巴什博奕。

威佐夫博弈:
有2堆物品,一堆m个一堆n个,两个人轮流取,每次可以取一堆中的任意个数或者取2堆中的相同个数,谁先把东西取完谁就获胜。
分析:我们用(ai,bi)(ai ≤ bi ,i=0,1,2,…,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲就输了,这种局势我们称为奇异局势,用(ak,bk)表示

我们例举一下前几个奇异局势:
(0,0) (1,2) (3,5) (4,7) (6,10) (8,13) (9,15) (11,18) (12,20)

例如(1,2):
1)取 1 中一个,那么后手取第二堆中两个。
2)取 2 中一个,那么后手在两堆中各取一个。
3)在 2 中取两个,那么后手在第一堆中取一个。
4)两堆中各取一个,那么后手在第二堆中取一个。
可以看出,不论先手怎么取后手都能赢,所以为奇异局势
我们来分析一下前几个奇异局势:
(0,0) (1,2) (3,5) (4,7) (6,10) (8,13) (9,15) (11,18) (12,20)

我们会发现他们的差值是递增的,从 0,1,2,3,4,5…..依次递增,而用数学方法分析发现局面中第一个值为前面局面中没有出现过的数字中的第一个值,比如第三个局面,前面出现了 0,1,2,那么第三个局面的第一个值为 3 ,比如第五个局面,前面出现了 0,1,2,3,4,5 ,那么第五个局面第一个值为6。再找规律的话我们会发现第一个值 = (int)(差值 * 1.618) ,而1.618 = (sqrt(5)+ 1) / 2 所以,只要满足(int)((b-a)*(sqrt(5)+ 1) / 2 )==a,此时(a,b)就是先手必输局势,否则先手必胜!

如果大家想深入学习博弈或者在队内负责数论的话,可以看一下有关Sg函数的概念和用法,这个用于不同的博弈题特别方便推出他们的奇异局势。

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
小牛和小客玩石子游戏,他们用n个石子围成一圈,小牛和小客分别从其中取石子,谁先取完谁胜,每次可以从一圈中取一个或者相邻两个,每次都是小牛先取,请输出胜利者的名字(小牛获胜输出XiaoNiu,小客获胜输出XiaoKe)(1 2 3 4 取走 2 13 不算相邻)

输入描述:
输入包括多组测试数据
每组测试数据一个n(1≤n≤1e9)
输出描述:
每组用一行输出胜利者的名字(小牛获胜输出XiaoNiu,小客获胜输出XiaoKe)
示例1
输入

2
3
输出

XiaoNiu
XiaoKe
解题思路:
简单的博弈,因为小牛先手,每次可取1个或2个,如果双方没有走错,那么除了2个以内石子的,先手必输。

#include<stdio.h>
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n<=2)
        printf("XiaoNiu\n");
        else
        printf("XiaoKe\n");
    }
    return 0;
}

链接:https://www.nowcoder.net/acm/contest/75/F
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
共有N堆石子,已知每堆中石子的数量,两个人轮流取石子,每次只能选择N堆石子中的一堆取一定数量的石子(最少取一个),取过子之后,还可以将该堆石子中剩余的石子随意选取几个放到其它的任意一堆或几堆上。等哪个人无法取子时就表示此人输掉了游戏。注意:一堆石子没有子之后,就不能再往此处放石子了。
假设每次都是小牛先取石子,并且游戏双方都绝对聪明,现在给你石子的堆数、每堆石子的数量,请判断出小牛能否获胜。
输入描述:
可能有多组测试数据(测试数据组数不超过1000)
每组测试数据的第一行是一个整数,表示N(1<=N<=10)
第二行是N个整数分别表示该堆石子中石子的数量。(每堆石子数目不超过100)
当输入的N为0时,表示输入结束
输出描述:
对于每组测试数据,输出Win表示小牛可以获胜,输出Lose表示小牛必然会败。
示例1
输入

3
2 1 3
2
1 1
0
输出

Win
Lose
备注:
提示:
例如:如果最开始有4堆石子,石子个数分别为3 1 4 2,而小牛想决定要先拿走第三堆石子中的两个石子(石子堆状态变为3 1 2 2),然后他可以使石子堆达到的状态有以下几种:
3 1 2 2(不再移动石子)
4 1 1 2(移动到第一堆一个)
3 2 1 2(移动到第二堆一个)
3 1 1 3(移动到第四堆一个)
5 1 0 2(全部移动到第一堆)
3 3 0 2(全部移动到第二堆)
3 1 0 4(全部移动到最后)
解题思路:
必败点是有偶数堆石子,并且每种石子数量的堆数都为偶数,必败点的推法不太清楚,这里不能详述。 根据博弈的PN图,每种必胜点都可以转化为必败点,所以只要存在有堆数为奇数的石子数量,就可以确定当前局面为必胜点。

#include<stdio.h>
#include<string.h>
int main()
{
    int n,a[1010];
    while(scanf("%d",&n),n)
    {
        int c;
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&c);
            a[c]++;
        }
        int f=0;
        for(int i=0;i<=100;i++)
        {
            if(a[i]%2)
            {
                f=1;
                break;
            }
        }
        if(f)
        printf("Win\n");
        else
        printf("Lose\n");
    }
    return 0;
}

运用尼姆定理的话可以这样:

#include<stdio.h>
#include<string.h>
int main()
{
    int n,c;
    while(scanf("%d",&n),n)
    {
        int d=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&c);
            if(c!=1)
            d^=c;
        }
        if(d!=0)
        printf("Win\n");
        else
        printf("Lose\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/watestill/article/details/79254581
今日推荐