SG函式
個人理解:SG函式是人們在研究博弈論的道路上邁出的重要一步,它把許多雜亂無章的博弈遊戲通過某種規則結合在了一起,使得一類普遍的博弈問題得到了解決。
從SG函式開始,我們不再是單純的同過找規律等方法去解決博弈問題,而是需要學習一些博弈論中基本的定理,來找到他們的共同特點
那麼就先介紹幾個最基本的定理(也可以叫常識)吧
基本定理
ICG遊戲
1.遊戲有兩個人蔘與,二者輪流做出決策。且這兩個人的決策都對自己最有利。
2.當有一人無法做出決策時遊戲結束,無法做出決策的人輸。無論二者如何做出決策,遊戲可以在有限步內結束。
3.遊戲中的同一個狀態不可能多次抵達。且遊戲不會有平局出現。任意一個遊戲者在某一確定狀態可以作出的決策集合只與當前的狀態有關,而與遊戲者無關。
滿足上述條件的問題我們稱之為ICG遊戲,ICG遊戲屬於組合遊戲
最典型的nim遊戲,就是一種ICG遊戲
必勝態與必敗態
定義P-position與N-position
P-position:必敗態(簡記為P
),即Previous-position,你可以直觀的認為處於這種狀態的人一定會輸
N-position:必勝態(簡記為N
),即Next-position,你可以直觀的理解為處於這種狀態的人一定會贏
這僅僅是最直觀的定義
更嚴謹的定義為:
- 無法移動的狀態(即terminal-position)為P
可以
移動到P的局面為N所有
移動都會進入N的局面為P
DAG(有向無環圖)中的博弈
在正式研究\(SG\)函式之前,我們先來研究一下DAG中的博弈
給定一張有向無環圖,在起始定點有一枚棋子,兩個頂尖聰明的人交替移動這枚棋子,不能移動的人算輸
不要小看這個遊戲,事實上,所有ICG問題都可以抽象為這種遊戲(即把初始局面看做頂點,把從一個狀態可以到另一個狀態之間連邊)
SG函式
下面我們來正式研究一下SG(Sprague-Grundy)函式
首先定義mex
運算,這是一種集合中的運算,它表示最小的不屬於集合的非負整數
例如\(mex\{1,2,3\}=0\),\(mex\{0,2\}=1\),\(mex\{0,1,2,3\}=4\),\(mex\{\}=0\)
對於給定的有向無環圖,定義每個點的SG函式為
\(SG(x)=mex \{\ SG(y)\ |\ x \ can\ go\ to\ y \}\)
然而單單一個這樣的空洞的函式是解決不了問題的,我們需要分析一下它的性質
- 所有匯點的\(SG\)函式為\(0\)
這個性質比較顯然,因為匯點的所有後繼狀態都是空集
- 當\(SG(x)=0\)時,該節點為必敗點
由\(SG\)函式的性質易知該節點的所有後繼節點\(SG\)值均不為\(0\)
滿足必敗態的定義
- 當$SG(x)\neq 0 $,該節點為必勝點
由\(SG\)函式的定義可知該節點的後繼節點中一定有一個節點\(SG=0\)
滿足必勝態的定義
這樣我們通過最基本的\(SG\)值的定義,我們就可以判斷出一個狀態是必勝態還是必敗態
這個問題實際上就是我們前面講的巴什博奕
如果這個問題再複雜一點呢?
當這個棋盤上有\(n\)個棋子的時候呢?
其實它們的分析思路是一樣的
當\(SG(x)=k\)時,它表明後繼狀態中含有\(SG(y)=1 \dots k-1\)
也就是說,我們從\(k\)可以轉移到\(1 \dots k-1\)中的任何一個狀態,而當前共有\(n\)個棋子。
這會讓你想到什麼?
nim取石子游戲!
那我們是不是也可以推出:
如果在nim遊戲中的\(n\)堆石子的\(SG\)值異或和不為\(0\)就說明先手必勝呢?
這是肯定的,因為當你打出nim遊戲的\(SG\)值表時就會發現,\(SG_{nim}(x)=x\)
是不是很神奇?
SG定理
SG函式的應用遠遠不止和巴什博奕與nim遊戲有關,我們回過頭來考慮能否把SG函式推廣開來
類比nim取石子游戲的思路,我們可不可以大膽設想:
遊戲的和的SG值是他們的SG值的xor
暫且不管這個結論對不對,我們設想一下,假如這個結論對的話,會有什麼後果.
我們可以將ICG問題對應到DAG上,然後直接通過SG函式之間的轉移而解決幾乎全部的問題
是不是很令人興奮?
更令人興奮的是,這個定理是正確的!
什麼?證明?
如果你是一個追求完美的人可以看這裡
如果你像我一樣連線性代數都不知道是什麼的話大概就是從DAG上歸納一下就好了吧
SG定理的應用
SG定理的應用非常的廣泛,幾乎所有的博弈類問題都有它的影子,本文僅僅是簡單的介紹一下這個定理,更深層次的應用以後會補充的
上面提到了SG函式,那麼SG函式的值是怎麼計算的呢?
很簡單,我們直接通過\(mex\)運算的定義就可以計算了
int F[MAXN];//可以轉移的狀態集合,一般題目會給出
int S[MAXN];//表示該點可以轉移到的狀態有哪些
int SG[MAXN];//該點的SG值
void GetSG()
{
for(int i=1;i<=N;i++)//列舉DAG中所有點
{
memset(S,0,sizeof(S));//初始化
for(int j=1;j<=limit&&F[j]<=i;j++)//limit表示轉移的集合的大小
S[SG[i-F[j]]]=1;
for(int j=1;;j++)
if(!S[j])
{SG[i]=i;break;}//根據定義計算SG函式
}
}
來一道裸題