博弈(部分)

三类经典博弈类型:巴什博奕、威佐夫博奕、尼姆博奕

取石子问题:

A:1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"Firstwin".

 Input

输入有多组.每组第1行是2<=n<2^31. n=0退出.

 Output

先取者负输出"Second win". 先取者胜输出"Firstwin". 
参看SampleOutput.

 Sample Input

2

13

10000

0

Sample Output

Second win

Second win

First win

 

这是一道Fibonacci’s Game(斐波那契博弈
斐波那契博弈模型,大致上是这样的:
有一堆个数为 n 的石子,游戏双方轮流取石子,满足:
1. 先手不能在第一次把所有的石子取完;
2. 之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。
约定取走最后一个石子的人为赢家,求必败态。

 

(转)分析:      
 n = 2时输出second;     
 n = 3时也是输出second; 
 n = 4时,第一个人想获胜就必须先拿1个,这时剩余的石子数为3,此时无论第二个人如何取,第一个人都能赢,输出first; 
 n = 5时,first不可能获胜,因为他取2时,second直接取掉剩下的3个就会获胜,当他取1时,这样就变成了n为4的情形,所以输出的是second;   
 n = 6时,first只要去掉1个,就可以让局势变成n为5的情形,所以输出的是first;      
 n = 7时,first取掉2个,局势变成n为5的情形,故first赢,所以输出的是first;     
 n = 8时,当first取1的时候,局势变为7的情形,第二个人可赢,first取2的时候,局势变成n为6得到情形,也是第二个人赢,取3的时候,second直接取掉剩下的5个,所以n= 8时,输出的是second;    
 …………      
 从上面的分析可以看出,n为2、3、5、8时,这些都是输出second,即必败点,仔细的人会发现这些满足斐波那契数的规律,可以推断13也是一个必败点。     
 借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。n=12时,只要谁能使石子剩下8且此次取子没超过3就能获胜。因此可以把12看成8+4,把8看成一个站,等价与对4进行"气喘操作"。又如13,13=8+5,5本来就是必败态,得出13也是必败态。也就是说,只要是斐波那契数,都是必败点。
所以我们可以利用斐波那契数的公式:fib[i] = fib[i-1] + fib[i-2],只要n是斐波那契数就输出second。

  1. #include <stdio.h>   
  2. __int64 f[100];  
  3. int main()  
  4. {  __int64 n,m,i;  
  5.    while((scanf("%I64d",&n)!=EOF),n)  
  6.     {  
  7.        f[0]=2;  
  8.        f[1]=3;  
  9.        for(i=2;i<=44;i++)  
  10.           f[i]=f[i-1]+f[i-2];  
  11.        for(i=0;i<=44;i++)  
  12.        {  
  13.            if(n==f[i])  
  14.            {  
  15.                 m=1;  
  16.                 break;  
  17.            }  
  18.            m=0;  
  19.        }  
  20.        if(m==1)  
  21.          printf("Second win\n");  
  22.        else  
  23.          printf("First win\n");  
  24.     }  
  25.    return 0;  
  26. }  
#include <stdio.h>
__int64 f[100];
int main()
{  __int64 n,m,i;
   while((scanf("%I64d",&n)!=EOF),n)
    {
       f[0]=2;
       f[1]=3;
       for(i=2;i<=44;i++)
          f[i]=f[i-1]+f[i-2];
       for(i=0;i<=44;i++)
       {
           if(n==f[i])
           {
                m=1;
                break;
           }
           m=0;
       }
       if(m==1)
         printf("Second win\n");
       else
         printf("First win\n");
    }
   return 0;
}


 B:有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
 
 
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
 
 
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
 
 
Sample Input
2 1
8 4
4 7
 
 
Sample Output
0
1
0
 
1、在一堆石子中取走任意多颗;
2、在两堆石子中取走相同多的任意颗;
约定取走最后一颗石子的人为赢家,求必胜策略。
两堆石头地位是一样的,我们用余下的石子数(a,b)来表示状态,并画在平面直角坐标系上。
和前面类似,(0,0)肯定是 P 态,又叫必败态。(0,k),(k,0),(k,k)系列的节点肯定不是 P 态,而是必胜态,你面对这样的局面一定会胜,只要按照规则取一次就可以了。再看 y = x 上方未被划去的格点,(1,2)是 P 态。k > 2 时,(1,k)不是 P 态,比如你要是面对(1,3)的局面,你是有可能赢的。同理,(k,2),(1 + k, 2 + k)也不是 P 态,划去这些点以及它们的对称点,然后再找出 y = x 上方剩余的点,你会发现(3,5)是一个 P 态,如此下去,如果我们只找出 a ≤ b 的 P 态,则它们是(0,0),(1,2),(3,5),(4,7),(6,10)……它们有什么规律吗?
忽略(0,0),很快会发现对于第 i 个 P 态的 a,a = i * (sqrt(5) + 1)/2 然后取整;而 b = a + i。居然和黄金分割点扯上了关系。
前几个必败点如下:(0,0),(1,2),(3,5),(4,7),(6,10),(8,13)……可以发现,对于第k个必败点(m(k),n(k))来说,m(k)是前面没有出现过的最小自然数,n(k)=m(k)+k。
判断一个点是不是必败点的公式与黄金分割有关:
m(k) = k * (1 + sqrt(5))/2
n(k) = m(k) + k

k=大—小

奇异局势是;int(k*(1+sqrt(5.0))/2)==m

谁面临奇异局势谁败

  1. #include<stdio.h>   
  2. #include<math.h>   
  3. int main ()  
  4. {  
  5.    intn,m,k,t;  
  6.   while(~scanf("%d%d",&n,&m))  
  7.   {   
  8.       if(n<m)  
  9.       {  
  10.              t=m;  
  11.              m=n;  
  12.              n=t;  
  13.       }  
  14.       k=n-m;  
  15.       if(int(k*(1+sqrt(5.0))/2)==m)  
  16.       printf("0\n");  
  17.       else  
  18.       printf("1\n");  
  19.     }  
  20.    return 0;  
  21. }  
  22.    
#include<stdio.h>
#include<math.h>
int main ()
{
   intn,m,k,t;
  while(~scanf("%d%d",&n,&m))
  { 
      if(n<m)
      {
             t=m;
             m=n;
             n=t;
      }
      k=n-m;
      if(int(k*(1+sqrt(5.0))/2)==m)
      printf("0\n");
      else
      printf("1\n");
    }
   return 0;
}
 


 

C题目大意:有n堆苹果,每堆有mi个。两人轮流取,每次可以从一堆苹果中取任意连续个苹果,最后取光者为输。Fra先下,问是否可以获胜 

首先给出结论:   先手胜当且仅当

 

1)所有堆石子数都为1且游戏的SG值为0,(2)存在某堆石子数大于1且游戏的SG值不为0

 

   证明:

 

1)若所有堆石子数都为1SG值为0,则共有偶数堆石子,故先手胜。

 

2

 

i)只有一堆石子数大于1时,我们总可以对该堆石子操作,使操作后石子堆数为奇数且所有堆得石子数均为1

 

ii)有超过一堆石子数大于1时,先手将SG值变为0即可,且总还存在某堆石子数大于1

 

因而,先手胜。

  1. #include<stdio.h>   
  2. int a[105];  
  3. int main()  
  4. {  
  5.     inti,ans,n,temp;  
  6.     while(scanf("%d",&n)!=EOF)  
  7.     {  
  8.         temp=0;//孤单堆   
  9.         for(i=0;i<n;i++)  
  10.         {  
  11.             scanf("%d",&a[i]);  
  12.             if(i==0)  
  13.                 ans=a[i];  
  14.             else  
  15.                 ans=ans^a[i];  
  16.             if(a[i]>1) temp=1;  
  17.         }  
  18.         if(temp==0)  
  19.         {  
  20.             if(n%2==1)  
  21.                printf("No\n");  
  22.             else  
  23.                printf("Yes\n");  
  24.             continue;  
  25.         }          
  26.         if(ans==0)  
  27.            printf("No\n");  
  28.         else  
  29.            printf("Yes\n");}      return 0;}  
#include<stdio.h>
int a[105];
int main()
{
    inti,ans,n,temp;
    while(scanf("%d",&n)!=EOF)
    {
        temp=0;//孤单堆
        for(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==1)
               printf("No\n");
            else
               printf("Yes\n");
            continue;
        }        
        if(ans==0)
           printf("No\n");
        else
           printf("Yes\n");}      return 0;}


D.巴什博弈各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、  本游戏是一个二人游戏;
2、  有一堆石子一共有n个;
3、  两人轮流进行;
4、  每走一步可以取走1…m个石子;
5、  最先取光石子的一方为胜;

巴什博弈:

只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m.

最后取光者得胜.

如果物品时1~m的话,先手必赢

物品时1+m~2*m的话,不管先手如何取后手必赢

所以如果n=(m+1)*任意倍数+s(s<=m)的话先手必赢,因为先手拿掉s的话,

就成了奇异局势,不管后手取多少(假设是x),

先手只要取m-x-1就一定会使最后石子变成后手面对1+m~2*m的局势这样

;先手必赢,反之后手必赢

  1.  #include<iostream>   
  2. using namespace std;  
  3. int main()  
  4. {  
  5.                intc,n,m,i,j;  
  6.                scanf("%d",&c);  
  7.                while(c--)  
  8.                {  
  9.                                scanf("%d%d",&n,&m);  
  10.                                if(n%(m+1))  
  11.                                printf("first\n");  
  12.                                else  
  13.                                printf("second\n");  
  14.                }  
  15.                return0;  
  16. }  
 #include<iostream>
using namespace std;
int main()
{
               intc,n,m,i,j;
               scanf("%d",&c);
               while(c--)
               {
                               scanf("%d%d",&n,&m);
                               if(n%(m+1))
                               printf("first\n");
                               else
                               printf("second\n");
               }
               return0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值