博弈入门---(巴什博弈,威佐夫博弈,nim博弈)

组合游戏定义:

       1、有且仅有两个玩家   

       2、游戏双方轮流操作    

       3、游戏操作状态是个有限的集合(比如:取石子游戏,石子是有限的,棋盘中的棋盘大小的有限的)  

       4、游戏必须在有限次内结束  

       5、当一方无法操作时,游戏结束。


(一)巴什博奕

        1.游戏规则:有一堆n个物品,两人轮流从堆中取物品,每次取 x 个 ( 1 ≤ x ≤ m)。最后取光者为胜。

        2.分析:由于一次至多取 m 个,当 n = m + 1时,如果先取者取了x个(1<=x<=m),后取者一定能取走n-x个(1<=n-x<=n),即能够一次拿走剩余的物品,此时,后取者必胜。因此,我们可以发现取胜的法则,当n=(m+1)*k+r(k为任意自然数,r<=m),此时,先取者第一次拿走r个物品,如果后取者拿走x(<=m)个,先取者只要拿走m+1-x个,保证剩下的为(m+1)的倍数,那么先取者必胜。现在,我们可以知道,如果 s = 0,那么后取者必胜,否则先取者必胜。

         对于巴什博奕,如果我们规定最后取关者失败,则需要满足(n-1)%(m+1)等于0。

        3.对于这类题,一般可以画出PN图,根据规律直接求解。 

        4.相关题目:(都是水题)

           hdu 1846 Brave Game 点击打开链接 

           代码实现如下:

#include
int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        if(n%(m+1)==0)
            printf("second\n");
        else printf("first\n");
    }
}



      hdu 2516 取石子游戏  点击打开链接  

       一个典型的斐波拉契博弈,在网上看到了一篇对斐波拉契博弈原理讲的很清楚的博文,可以参考一下:http://blog.csdn.net/dgq8211/article/details/7602807

      代码实现如下:

#include
int main()
{
    int f[48];//n趋于48时,f[n]趋于int的最大值
    f[0] = 0;
    f[1] = 1;
    for(int i=2;i<48;i++)
    {
        f[i]=f[i-1]+f[i-2];
    }
    int n;
    while(~scanf("%d",&n)&&n)
    {
        int flag=0;
        for(int i=3;i<48;i++)
            if(n==f[i])
            {
                flag=1;
                break;
            }
        if(flag)
            printf("Second win\n");
        else
            printf("First win\n");
    }
}




        hdu 2149 Public Sale 点击打开链接

        代码实现如下:

#include
int main()
{
    int m,n;
    int a[105];
    while(~scanf("%d%d",&m,&n))
    {
        if(m%(n+1)==0)//先手必败
            printf("none\n");
        else
        {
            if(n>=m)//当n>=m时,无论lele叫价多少都可以获得这块地
            {
                for(int i=m;i<=n;i++)
                    if(i==n)printf("%d\n",i);
                    else printf("%d ",i);
            }
            else
            {
                int flag=0;//只是保证输出的末尾没有空格
                for(int i=1;i<=n;i++)
                    if((m-i)%(n+1)==0)//留下n+1的倍数才能保证必胜
                {
                    if(!flag)
                        printf("%d",i);
                    else printf(" %d",i);
                    flag=1;
                }
                printf("\n");
            }
        }
    }
}


      hdu 2147 kiki's game 点击打开链接

      画出PN图

P N P N P
N N N N N
P N P N P
N N N N N
P N P N P

         由图可以看出,当m或者n有一个是偶数时,先手就能必胜。

         代码实现如下:

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

(二)威佐夫博弈
        1.游戏规则:有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
        2.分析:

  •  在分析前我们先来看一下奇异局势的概念及性质。
              概念:
                     我们用(ak,bk)(ak ≤ bk ,k=0,1,2,...,n)表示两堆物品的数量,并称这个为局势。
                     如果甲面对一种局势已经可以判断他输了,这种局势就是奇异局势,比如(0,0),(1,2)等。
              性质:
                      1、任何自然数都包含在一个且仅有一个奇异局势中。  
                     2、任意操作都可将奇异局势变为非奇异局势。
                     3、采用适当的方法,可以将非奇异局势变为奇异局势。

  •   我们可以根据条件来分析这个博弈中的奇异局势。
             我们可以先找到每个奇异局势。
            (0,0)先手失败。
             (1,2)先手失败。
              {
                       对于先手有四种取法。
                       1)取 1 中的一个,那么后手取第二堆中两个。
                       2)取 2 中一个,那么后手在两堆中各取一个。
                       3)取2 中的两个,那么后手在第一堆中取一个。
                       4)两堆中各取一个,那么后手在第二堆中取一个。
                       由此,先手必败。
              }
               接着分析一下可以发现 :
              (3,5)先手失败。
              (4,7)先手失败。
              (6,10)先手失败。
              (8 ,13)先手失败。
              到这里,我们会发现bk-ak的值是不断递增的,分别为1,2,3,4,5,...,n。
              并且由奇异局势的性质一我们可以看出,每个局势中的ak总是前面局势中没有出现过的第一个值。比如第三个局势,前面出现了 0 1 2,那么第三个局势的第一个值为 3               比如第五个局势,前面出现了 0 1 2 3 4 5 ,那么第五个局势第一个值为6。
              根据分析我们可以推出:bk=ak+k。
              那么我们该如何找出奇异局势呢?
                     大量的数据分析之后,我们可以发现ak=[k*1.618]([n]表示不超过n的最大整数,在cmath库函数中有floor(x)表示向下取整,而在计算中,直接int强制转换求出值即可),而1.618=(1+√5)/2。
                      所以我们可以先求出a和b的差值k,再根据公式将当前差值k的ak求出来,即ak=[k*(1+√5)/2],且bk=ak+k。如果min(a,b)=ak且max(a,b)=bk,则当前局势为奇异局势,即先手必败。


       3.威佐夫博弈常见的两类问题


            1)给定一个局势,求先手输赢。
                 根据分析,这个问题是很容易解决的。首先求出差值abs(a-b),如果abs(a-b)* (1+√5)/2 ==min(a,b) 的话先手失败,否则先手取胜
            2)给定一个局势,求先手输赢,并且在先手赢的情况下求解他第一次取后两堆剩余的个数。
                  >     两边同时取的情况:当两边同时取的时候,两边的差值是不会改变的,所以我们可以根据差值计算出其中较小的值,加上差值就是较大的那个值。原来两堆的个数减去用差值求出来的两堆的个数即为答案。(当且仅当求出来的最小值大于原来堆数的最小值时成立)
                 >     在其中一堆取的情况:可以取任意一堆,那么其差值也是不定的,但是我们可以枚举差值,差值范围是(0,两堆石子较大值),再根据上面的理论判断该取法是否合理即可。
         4.相关题目:

          poj 1067 取石子游戏 点击打开链接

          代码实现如下:

#include
#include
int main()
{
    int a,b;
    while(~scanf("%d%d",&a,&b))
    {
        int m=abs(a-b);
        a=a
        
          hdu 2177 取(2堆)石子游戏 点击打开链接

          代码实现如下:

#include
#include
#include
using namespace std;
int main()
{
    int a,b,temp,temp2,k,i, tmp;
    while(scanf("%d%d",&a,&b),a+b)
    {
        if(a>b)
            swap(a,b);
        k=b-a;
        temp=k*(1.0+sqrt(5.0))/2.0;
        if(a==temp)//先手必败
            printf("0\n");
        else
        {
            printf("1\n");
            if(abs(temp-a)==abs(temp+k-b)&&temp


(三)尼姆博弈
           1.游戏规则:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获                    胜。
            2.分析:这个问题分析起来是比较复杂的,在网上看到了一篇讲的很棒的博文,顺便偷个懒,有空再自己写写。附上原博文的网址点击打开链接 
            3.相关题目:
            POJ 2234 Matches Game 点击打开链接
            一道最基础的尼姆博弈题。只需要简单异或就行了,最后判断是否为零,若是零就是后手必胜,反之先手。

            代码实现如下:

#include
int main()
{
    int c;
    while(~scanf("%d",&c))
    {
        int n,sum=0;
        for(int i=0;i

             hdu 2176 取(m堆)石子游戏  点击打开链接

             代码实现如下:

#include
int main()
{
    int m;
    int a[200005];
    while(~scanf("%d",&m)&&m)
    {
        int ans=0;
        for(int i=0;i(ans^a[i]))//我们可以知道,对任意的当前状态S,只需将某堆石头a[i]变为ans^a[i]即可使得整个局面的SG值为0,即变为必败态,所以只需要a[i]比ans^a[i]大即为合理状态。
                    printf("%d %d\n",a[i],ans^a[i]);
            }
        }
    }
}

nim博弈进阶——阶梯博弈

      1.游戏规则:在一列阶梯上进行博弈,每个阶梯上放着若干个点,两个人进行博弈,每一步则是将一个阶梯上的若干个点( 大于等于一个 )移到前面的阶梯上去,最后无法移动的人输。

         2.分析:【转自网络点击打开链接】阶梯博弈可以转换为奇数位置上的nim博弈。原因如下:假设我们是先手,所给的阶梯石子状态的奇数堆做Nim先手能必胜。按照能赢的步骤将奇数堆的石子移动到偶数堆...如果对手也是移动奇数堆..我们继续移动奇数堆..如果对手将偶数堆的石子移动到了奇数堆..那么我们紧接着将对手所移动的这么多石子从那个奇数堆移动到下面的偶数堆...两次操作后...相当于偶数堆的石子向下移动了几个..而奇数堆依然是原来的样子...即为必胜的状态...就算后手一直在移动偶数堆的石子到奇数堆..我们就一直跟着他将石子继续往下移..保持奇数堆不变...如此做下去..我可以跟着后手把偶数堆的石子移动到0..然后你就不能移动这些石子了...所以整个过程..将偶数堆移动到奇数堆不会影响奇数堆做Nim博弈的过程..整个过程可以抽象为奇数堆的Nim博弈...其他的情况...先手必输的...类似推理...只要判断奇数堆做Nim博弈的情况即可...为什么是只对奇数堆做Nim就可以...而不是偶数堆呢?因为如果是对偶数堆做Nim...对手移动奇数堆的石子到偶数堆..我们跟着移动这些石子到下一个奇数堆...那么最后是对手把这些石子移动到了0..我们不能继续跟着移动...就只能去破坏原有的Nim而导致胜负关系的不确定...所以只要对奇数堆做Nim判断即可知道胜负情况。



你可能感兴趣的:(博弈)