1. 程式人生 > >三大經典博弈 尼姆博奕 + 巴仕博弈 + 威佐夫博弈 +SG函式

三大經典博弈 尼姆博奕 + 巴仕博弈 + 威佐夫博弈 +SG函式

第一,尼姆博奕(Nimm Game)

一,特例分析

有三堆各若干個物品,兩個人輪流從某一堆取任意多的

物品,規定每次至少取一個,多者不限,最後取光者得勝。

我們用(a,b,c)表示某種局勢,首先(0,0,0)顯然是奇異局勢,無論誰面對奇異局勢,都必然失敗。

第二種奇異局勢是(0,n,n),只要與對手拿走一樣多的物品,最後都將導致(0,0,0)。

 任何奇異局勢(a,b,c)都有a^b^c =0。

如果我們面對的是一個非奇異局勢(a,b,c),要如何變為奇異局勢呢? 只要將其中一個是

變成其他兩個數的異或就行了。

二,例題

題目1:今有若干堆火柴,兩人依次從中拿取,規定每次只能從一堆中取若干根,可將一堆全取走,但不可不取,

最後取完者為勝,求必勝的方法。 

 備註: 若所有火柴數異或為0,則該狀態被稱為利他態,用字母T表示;否則, 為利己態,用S表示。

[定理1]:對於任何一個S態,總能從一堆火柴中取出若干個使之成為T態。

[定理2]:T態,取任何一堆的若干根,都將成為S態。

[定理 3]:S態,只要方法正確,必贏。

[定理4]:T態,只要對方法正確,必敗。

題目2:今有若干堆火柴,兩人依次從中拿取,規定每次只能從一堆中取若干根,可將一堆全取走,但不可不取,

最後取完者為負,求必勝的方法。 

備註:若一堆中僅有1根火柴,則被稱為孤單堆。若大於1根,則稱為充裕堆。0:沒有富裕堆,1:有一個富裕堆  ,

 2:有大於等於個富裕堆

[定理1]:T2態必輸。

[定理2]:S0 即僅有奇數個孤單堆,必敗,T0  態必勝。

[定理3]:S1態,只要方法正確,必勝

[定理4]:S2態,只要方法正確,必勝.  

四,一般情況

有N堆石子,其中第i堆有Pi顆石子,每次從某一堆裡選出若干石子去掉(但不能不去石子),兩人輪流取石,

誰不能繼續取誰就輸了。

備註: 若所有火柴數異或為0,則該狀態被稱為利他態,用字母T表示;否則, 為利己態,用S表示。

若只有一堆石子,為T狀態

若有m堆石子,每堆有k顆石子, m堆為奇數時 ,為T狀

定理:
   對於一個局面,令H=P1^P2^P3^… ^Pn。若H=0則為T局面,否則為s局面

總結:

 有n堆各若干個物品,兩個人輪流從某一堆取任意多(或者最多m個,只需把每堆%m)的物品,規定每次至少

取一個,多者不限,最後取光者得勝。  把每堆數量求異或a1^a2^...^ai'^...^an,結果為零則先手必輸,否則必贏

三,Nim問題的擴充套件
問題:取石子問題
    有N堆石子,其中第i堆有Pi顆石子,每次去掉某一堆裡最多m棵石子(m>0),兩人輪流取石,

誰不能繼續取誰就輸了。什麼情況下先手必勝,什麼情況下後手必勝?

解析:

    將P1,P2,P3, … Pn 對m+1求餘得到q1,q2,q3, …,qn然後符合定理一的結果,記H=q1^q2^q3^ …^qn 。

若H=0則為T局面,否則為s局面。

備註: s: 為利己態,先手必勝,非奇異態。

              T:為利他態,先手必敗,奇異態。

四,Nimk問題的取石子方法
問題:取石子問題
    有N堆石子,其中第i堆有Pi顆石子,每次可以從最多K堆中選出若干石子去掉(但不能不去石子),

兩人輪流取石,誰不能繼續取誰就輸了。

什麼情況下先手必勝,什麼情況下後手必勝?

K=1,為Nim問題。

對於K>1的情況,我們令把P1~Pn這n個數,轉成二進位制,然後每位分別相加,每位最後結果mod (K+1)即可。

如果每一位結果都是0,則為T局面,否則是s局面

第二:Sprague-Grundy函式(SG函式)

一,概述:

如果我們把遊戲中的某一個局面看作一個頂點,把局面之間的轉換用邊來表示,

那麼很多遊戲都可以轉化成圖遊戲模型。圖遊戲模型 給定有向無環圖G=(V,E)和一個起始點,

雙方輪流行動。每個人每次可以從當前點出發沿著一條有向邊走到另外一個點。誰無法走了誰就輸。

一些圖遊戲可以通過Sprague-Grundy函式來判定先手的勝負情況(簡稱SG函式)。

SG函式 一個圖G=(V,E)的SG函式g,是定義在v上的一個非負整數函式:

g(x)=min{key>=0 | key≠g(y) for <x,y>∈E}

key屬於[0,n](n為頂點個數)

如果x的出度為0,那麼g(x)=0。(邊界條件)

g(x)就是x的後繼點的SG值中沒有出現過的最小值

解析:

這樣定義有什麼好處呢?我們把一個圖的當前狀態值定義為遊戲者處在的這個點的SG值。

如果遊戲者處在一個點x,g(x)≠0。那麼0, 1, …, g(x)-1這些數必然都出現在x的後繼節點的SG值中

而遊戲者可以走到這些點中的任意一個。也就是說:遊戲者可以通過一步走棋把圖的當前狀態值任意的減小

(當然必須保證狀態值始終>=0)。如果遊戲者處在一個點x,g(x)=0。那麼遊戲者無論如何移動,

下一個點的SG值都不等於0。

定理:

   對於一個圖遊戲,如果圖的當前狀態等於0,那麼先手必敗,否則必勝。

擴充套件:

   多圖遊戲 有多個圖,每個圖都有一個當前節點。兩個遊戲者輪流行動。每個人每次可以把某一個圖中的

當前節點沿著該點連出的有向邊移動到另一個點。無法移動的那個人輸。

結論:

   設這些圖的當前狀態值分別是a1, a2, …, ak,如果:a1 ^ a2 ^ … ^ ak = 0,那麼先手必敗,否則必勝。

三,定理

1、 所有終結點都是必敗點P

2、所有一步能走到必敗點P的就是N點;

3、通過一步操作只能到N點的就是P點;

第三:巴仕博弈(Bash Game)

一,題目:

  只有一堆n個物品,兩個人輪流從這堆物品中取物,規 定每次至少取一個,最多取m個。最後取光者得勝。

二,分析

1,如果n=m+1,那麼由於一次最多隻能取m個,所以,無論先取者拿走多少個,

後取者都能夠一次拿走剩餘的物品,後者取勝。必敗

2,法則:如果n=(m+1)*r+s,(r為任意自然數,s≤m),那麼先取者要拿走s個物品,如果後取者拿走k(≤m)個,

那麼先取者再拿走m+1-k個,結果剩下(m+1)(r-1)個,以後保持這樣的取法,那麼先取者肯定獲勝。

總之,要保持給對手留下(m+1)的倍數,就能最後獲勝。必勝局

3,備註:

P點:即必敗點,某玩家位於此點,只要對方無失誤,則必敗;

N點:即必勝點,某玩家位於此點,只要自己無失誤,則必勝。

第四:威佐夫博弈(Wythoff Game)

一,題目

有兩堆各若干個物品,兩個人輪流從某一堆或同時從兩堆中取同樣多的物品,規定每次至少取一個,

多者不限,最後取光者得勝

二,解析

1,。我們用(a[k],b[k])(a[k] ≤ b[k] ,k=0,1,2,...,n)表示兩堆物品的數量並稱其為局勢。

2,如果甲面對(0,0),那麼甲已經輸了,這種局勢我們稱為奇異局勢。

3,奇異局(舉例)

首先列舉人們已經發現的前幾個奇異局勢:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)

(8,13)、(9,15)、(11,18)、(12,20)。

通過觀察發現:a[0]=b[0]=0,a[k]是未在前面出現過的最小自然數,而 b[k]= a[k] + k。

4,奇異局勢有如下三條性質:

1)任何自然數都包含且僅包含在一個奇異局勢中。

2)任意操作都可以使奇異局勢變為非奇異局勢。

3)必有一種操作可以使非奇異局勢變為奇異局勢。

5,奇異局勢公式:

a[k]=[k*(1+√5)/2],b[k]=a[k]+k。

(k=0,1,2......,[ ]表示取整)

有趣的是,式中的(1+√5)/2正是黃金分割比例。

6,判斷

可以看出,如果兩人都採取正確的操作,那麼對於非奇異局勢,先拿者必勝,對於奇異局勢,

先拿者必敗。

第五,SG函式模板

//模板:(SG函式)
int f[N],sg[N],hash[N];     
void getSG(int n)
{
    int i,j;
    memset(sg,0,sizeof(sg));
    for(i=1;i<=n;i++)
    {
        memset(hash,0,sizeof(hash));
        for(j=1;f[j]<=i;j++)
            hash[sg[i-f[j]]]=1;
        for(j=0;j<=n;j++)    
        {
            if(hash[j]==0)
            {
                sg[i]=j;
                break;
            }
        }
    }
}

第六,威佐夫博弈模板

//威佐夫博弈模板
#include <stdio.h>
#include <math.h>
const double Gsr=(1+sqrt(5.0))/2;
void swap(int &a,int &b)
{
    int t=b;
    b=a;
    a=t;
}
int main()
{
    int a,b;
    while(~scanf("%d%d",&a,&b))
    {
        if(a>b)
            swap(a,b);
        if(a == (int)(Gsr*(b-a))) //奇異局勢,先拿者輸
            puts("First Lose");
        else
            puts("First Win");
    }
    return 0;
}