博弈简单介绍

一.巴什博弈:

1、问题模型:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个,最后取光者得胜。
2、解决思路:当n=m+1时,由于一次最多只能取m个,所以无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜,所以当一方面对的局势是n%(m+1)=0时,其面临的是必败的局势。所以当n=(m+1)*r+s,(r为任意自然数,s≤m)时,如果先取者要拿走s个物品,如果后取者拿走x(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。
结论:n%(m+1)!=0则先手赢
3、变形:条件不变,改为最后取光的人输。
结论:当(n-1)%(m+1)==0时后手胜利。
因为:输的人最后必定只抓走一个,如果>1个,则必定会留一个给对手。因为当n=m+1+1时,假设先手拿走k个,后手只要拿走m+1-k个就会只剩下一个给先手,此时先手会输。所以当一方面对的局势是(n-1)%(m+1)=0时,其面临的是必败局势。而若n=(m+1)*r+s,(s>1)那么先手只要拿掉s-1个,即可保证胜利。
综上,对于巴什博奕用以下方法判断胜负:
对于先取光获胜:n%(m+1)!=0则先手赢;
对于先取光失败:当(n-1)%(m+1)==0时后手胜利。
 

二、威佐夫博奕

1、问题模型:有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
2、解决思路:设(ai,bi)(ai ≤bi ,i=0,1,2,…,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。任给一个局势(a,b),如下公式判断它是不是奇异局势: ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,…,n 方括号表示取整函数)。(证明见百度百科)
3、满足上公式的局势性质:

  • (1)任何自然数都包含在一个且仅有一个奇异局势中。
  • 由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。所以性质成立。
  • (2)任意操作都可将奇异局势变为非奇异局势。
  • 若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势
  • (3)采用适当的方法,可以将非奇异局势变为奇异局势。
  • 假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了奇异局势(0,0);如果a = ak ,b > bk 那么,取走b - bk个物体,即变为奇异局势;如果 a = ak ,b<bk 则同时从两堆中拿走a-a[b-a] 个物体变为奇异局势( a[b-a], b-a+a[b-a]);如果a > ak ,b= ak + k 则从第一堆中拿走多余的数量a - ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k)从第二堆里面拿走 b - bj 即可;第二种,a=bj (j < k)从第二堆里面拿走 b - aj 即可。

4、结论:两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。
若给出两堆石子,若先手赢输出1,有以下代码:

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
	int a,b;
	while(cin>>a>>b)
	{
		if(a>b)
			swap(a,b);
		double k=b-a;
		double x=(1+sqrt(5.0))/2;
		int t=(k*x+k);
		if(t==b)
			cout<<0<<endl;
		else
			cout<<1<<endl;
	}
	return 0;
}

解法2

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int a,b;
int main()
{
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        if(a>b)
            swap(a,b);
        printf("%d\n",int((1+sqrt(5.0))/2*(b-a))!=a);
    }
    return 0;
}

 

三.Fibonacci博弈

问题模型:有一堆个数为n的石子,游戏双方轮流取石子,满足:

  • (1)先手不能在第一次把所有的石子取完;
  • (2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。 约定取走最后一个石子的人为赢家。

解决方法:当n为Fibonacci数时,先手必败。即存在先手的必败态当且仅当石头个数为Fibonacci数。

#include <bits/stdc++.h> 
using namespace std; 
const int N = 50;   
int f[N];  

void Init() 
{ 
   f[0] = f[1] = 1; 
   for(int i=2;i<N;i++) 
       f[i] = f[i-1] + f[i-2]; 
}   
int main() 
{ 
   Init(); 
   int n; 
   while(scanf("%d",&n)!=EOF) 
   { 
       if(n == 0) break; 
       bool flag = 0; 
       for(int i=0;i<N;i++) 
       { 
           if(f[i] == n) 
           { 
                flag = 1; 
                break; 
           }
       } 
        if(flag) puts("Sha"); 
       else  puts("Xian"); 
   } 
   return 0; 
}

 

四.尼姆博弈

问题的基本模型为:有三堆各若干个物品(个数分别为a,b,c),两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。这里的三个一般可推广至n个

解决思路:先手必败态等价于a^b^c=0(^表示异或运算)。

推广至n个物品:

#include<iostream>
using namespacestd;
int main()
{
    int n,ans,temp;
    while(scanf(“%d”,&n)!=EOF)
    {
        temp=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&ans);
            temp^=ans;
        }
        if(temp==0)  cout<<"后手必胜"<<endl;
        else cout<<"先手必胜"<<endl;
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值