1. 程式人生 > >博弈論(巴什博奕,威佐夫博弈,尼姆博弈,斐波那契博弈)

博弈論(巴什博奕,威佐夫博弈,尼姆博弈,斐波那契博弈)

一.  巴什博奕(Bash Game):

  A和B一塊報數,每人每次報最少1個,最多報4個,看誰先報到30。這應該是最古老的關於巴什博奕的遊戲了吧。

其實如果知道原理,這遊戲一點運氣成分都沒有,只和先手後手有關,比如第一次報數,A報k個數,那麼B報5-k個數,那麼B報數之後問題就變為,A和B一塊報數,看誰先報到25了,進而變為20,15,10,5,當到5的時候,不管A怎麼報數,最後一個數肯定是B報的,可以看出,作為後手的B在個遊戲中是不會輸的。

那麼如果我們要報n個數,每次最少報一個,最多報m個,我們可以找到這麼一個整數k和r,使n=k*(m+1)+r,代入上面的例子我們就可以知道,如果r=0,那麼先手必敗;否則,先手必勝。

巴什博奕:只有一堆n個物品,兩個人輪流從中取物,規定每次最少取一個,最多取m個,最後取光者為勝。

程式碼如下:

#include <iostream>
using namespace std;
int main()
{
    int n,m;
    while(cin>>n>>m)
      if(n%(m+1)==0)  cout<<"後手必勝"<<endl;
      else cout<<"先手必勝"<<endl;
    return 0;
}

例題有:HDU4764  Stone:

題目大意:Tang和Jiang輪流寫數字,Tang先寫,每次寫的數x滿足1<=x<=k,Jiang每次寫的數y滿足1<=y-x<=k,誰先寫到不小於n的數算輸。

結論:r=(n-1)%(k+1),r=0時Jiang勝,否則Tang勝。

二.  威佐夫博弈(Wythoff Game):

有兩堆各若干的物品,兩人輪流從其中一堆取至少一件物品,至多不限,或從兩堆中同時取相同件物品,規定最後取完者勝利。

直接說結論了,若兩堆物品的初始值為(x,y),且x<y,則另z=y-x;

記w=(int)[((sqrt(5)+1)/2)*z  ];

若w=x,則先手必敗,否則先手必勝。

程式碼如下:

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
int main()
{
    int n1,n2,temp;
    while(cin>>n1>>n2)
    {
        if(n1>n2)  swap(n1,n2);
        temp=floor((n2-n1)*(1+sqrt(5.0))/2.0);
        if(temp==n1) cout<<"後手必勝"<<endl;
        else cout<<"先手必勝"<<endl;
    }
    return 0;
}


三.  尼姆博弈(Nimm Game):

尼姆博弈指的是這樣一個博弈遊戲:有任意堆物品,每堆物品的個數是任意的,雙方輪流從中取物品,每一次只能從一堆物品中取部分或全部物品,最少取一件,取到最後一件物品的人獲勝。

結論就是:把每堆物品數全部異或起來,如果得到的值為0,那麼先手必敗,否則先手必勝。

程式碼如下:

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
int main()
{
    int n,ans,temp;
    while(cin>>n)
    {
        temp=0;
        for(int i=0;i<n;i++)
        {
            cin>>ans;
            temp^=ans;
        }
        if(temp==0)  cout<<"後手必勝"<<endl;
        else cout<<"先手必勝"<<endl;
    }
    return 0;
}


四.  斐波那契博弈:

有一堆物品,兩人輪流取物品,先手最少取一個,至多無上限,但不能把物品取完,之後每次取的物品數不能超過上一次取的物品數的二倍且至少為一件,取走最後一件物品的人獲勝。

結論是:先手勝當且僅當n不是斐波那契數(n為物品總數)

如HDU2516

#include <iostream>  
#include <string.h>  
#include <stdio.h>  
using namespace std;  
const int N = 55;    
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(cin>>n)  
    {  
        if(n == 0) break;  
        bool flag = 0;  
        for(int i=0;i<N;i++)  
        {  
            if(f[i] == n)  
            {  
                flag = 1;  
                break;  
            }  
        }  
        if(flag) puts("Second win");  
        else     puts("First win");  
    }  
    return 0;  
}