各種博弈論詳解


博弈論

理論鋪墊:

定義P-positionN-position:其中P代表Previous,N代表Next。直觀的說,上一次move的人有必勝策略的局面是P-position,即P-position代表先手必輸,N-position代表先手必贏。

1.公平組合博弈(ICG)

定義:

1.兩個人輪流參與; 2.游戲局面有限; 3.同一個局面兩個人操作完全相同; 4.無法進行操作的人輸; 5.在有限步內結束。

模型:給定一個有向無環圖和一個起始頂點上的一枚棋子,兩名選手交替的將這枚棋子沿有向邊進行移動,無法移動者判負。

解題思路:SG函數(Sprague-Grudy)

SG定理:

1:給定一個有限子集 S ⊂ N,令mex S為沒有出現在S中的最小自然數。例:mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

2:對於一個給定的有向無環圖,定義關於圖的每個頂點的Sprague-Garundy函數g如下:sg(x)=mex{ sg(y) | y是x的后繼 }。即y可以一步操作到x;

SG函數性質:

1:SG值為0必敗態, 否則為必勝態

2:對於一個sg(x)=0的頂點x,它的所有后繼y都滿足g(y)!=0。同理,對於一個sg(x)!=0的頂點,必定存在一個后繼y滿足g(y)=0。

例:有三堆石子,第一堆石子的可以取1、2、3個石子,則:sg[x] = x % 4;第二堆可以取奇數個石子,則sg[x] = n % 2;第三堆有n個石子,其為Nim博弈,sg值為n,將三堆的sg值全部異或一下,判斷是不是0,是0則先手必敗(推廣到n堆其結論一樣適用)。

SG函數求法

其sg函數可以通過下面的模板來求:

//f[]:可以取走的石子個數
//sg[]:0~n的SG函數值
//hash[]:mex{}
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++)    //求mes{}中未出現的最小的非負整數
        {
            if(hash[j]==0)
            {
                sg[i]=j;
                break;
            }
        }
    }
}

記憶化搜索求SG函數,判斷該點的sg值是否為-1,如果是,則調用函數:

//注意 S數組要按從小到大排序 SG函數要初始化為-1 對於每個集合只需初始化1遍
//n是集合s的大小 f[i]是定義的特殊取法規則的數組
int s[110],sg[10010],n;
void SG_dfs(int x)
{
    int i;
    if(sg[x]!=-1)
        return;
    bool vis[maxn];
    memset(vis,0,sizeof(vis));
    for(i=1;i<=k;i++)
    {
        if(x>=f[i])
        {
            if(sg[x-f[i]]==-1)
                SG_dfs(x-f[i]);
            vis[sg[x-f[i]]]=1;
        }
    }
    int e;
    for(i=0;;i++)
        if(!vis[i])
        {
            sg[x]=i;
            break;
        }
}

AC代碼:

#include<bits/stdc++.h>

using namespace std;
const int maxn=1e4+5;
int f[101],sg[maxn];
int k,T,tot=0,temp,inf;
void SG_dfs(int x)
{
    int i;
    if(sg[x]!=-1)
        return;
    bool vis[maxn];
    memset(vis,0,sizeof(vis));
    for(i=1;i<=k;i++)
    {
        if(x>=f[i])
        {
            if(sg[x-f[i]]==-1)
                SG_dfs(x-f[i]);
            vis[sg[x-f[i]]]=1;
        }
    }
    int e;
    for(i=0;;i++)
        if(!vis[i])
        {
            sg[x]=i;
            break;
        }
}

int main()
{
    //ios::sync_with_stdio(false);
    while(scanf("%d",&k)&&k)
    {
        //memset(f,0,sizeof(f));
        memset(sg,-1,sizeof(sg));
        sg[0]=0;
        for(int i=1;i<=k;++i){
            scanf("%d",&f[i]);
        }
        //sort(f+1,f+k+1);
        scanf("%d",&T);
        while(T--)
        {
            tot=0;
            scanf("%d",&temp);
            for(int i=1;i<=temp;++i){
                scanf("%d",&inf);
                if(sg[inf]==-1)
                    SG_dfs(inf);
                tot^=sg[inf];        
            }
            !tot?cout<<"L":cout<<"W";
        }
        cout<<endl;
    }
    system("pause");
    return 0;
}

2.Bash(巴什博弈)

定義:有一堆共n個物品,兩個人輪流從中間取,每次最多取m個物品,先取完的人贏。

解題思路:n對(m+1)取模,模不為0,則先手必勝,模為0,則先手必敗(由SG函數知)。

3.Nim博弈(尼姆博弈)

定義:有n堆物品,兩人輪流從每堆中取物品,每次最少取一個,無上限,先取完者勝利。

解題思路:所有堆的個數相異或,如果結果為0,則先手必敗,不為0則先手必勝。必勝走法的方案數:將總的異或結果同每一堆相異或,如果結果小於該堆的數量,則可以從該堆取出x-x^tot個;

4.Wythoff Game(威佐夫博弈)

定義:有兩堆若干個物品,兩人輪流從一堆中取若干的物品或兩堆取同樣多的額物品,至少取一個,先取完的為勝。

解題思路:令當前局勢(Ak,Bk),則當前態勢為先手必敗態當且僅當Ak=((sqrt(5)+1)/2)*k、Bk=Ak+k;取出多少個則直接暴力循環判斷是不是必敗態即可

5.Fibonacci博弈

定義:有一堆個數為n的石子,雙方輪流取石子,先手不能一次把所有石頭取完,且后者取得石子的數目不能超過前者所取數目的2倍(可以包含1和2倍)。

解題思路:判斷該數是不是裴波那契數列,如果是,則先手必敗,否則先手必勝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM