(转)博弈 SG函数
此文為以下博客做的摘要:
https://blog.csdn.net/strangedbly/article/details/51137432
----------------------------------------------------------------------------------------
1、定義P-position和N-positon
P表示Previous,N表示Next。
即上一個移動的人有必勝策略的局面是P-position,“先手必敗”或“后手必勝”,現在移動的人有必勝策略的局面是N-positon,“后手必敗”或“先手必勝”。下面為更嚴謹的定義:
1、無法進行任何移動的局面(即terminal position)是P-position;
2、可以移動到P-position的局面是N-position;
3、所有移動都導致N-position的局面是P-position。
根據定義,不重現局面,那么positions的集合可以進行拓撲排序,并且能通過定義判斷為P-position還是N-position。
2、Nim游戲
對于一個Nim游戲的局面(a1, a2, ...an),它是P-position當且僅當 a1^a2^...^an = 0,其中 ^ 表示異或運算。
要證明這個定理,基本上是證明這種運算符合上述position的定義。即證明三個命題:1、這個判斷將所有無法移動的局面判為P-position;2、被判為N-position的局面一定可以移動到某個P-position;3、被判為P-position的局面無法移動到某個P-position。
第一個命題:無法移動的局面只有全為0,即異或仍為0。
第二個命題:對于某個局面(a1,a2,...,an),若a1^a2^...^an!=0,一定存在某個合法的移動,將ai改變成ai'后滿足a1^a2^...^ai'^...^an=0。不妨設a1^a2^...^an=k,則一定存在某個ai,它的二進制表示在k的最高位上是1(否則k的最高位那個1是怎么得到的)。這時ai^k<ai一定成立。則我們可以將ai改變成ai'=ai^k,此時a1^a2^...^ai'^...^an=a1^a2^...^an^k=0。
第三個命題:對于某個局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某個合法的移動,將ai改變成ai'后滿足a1^a2^...^ai'^...^an=0。因為異或運算滿足消去率,由a1^a2^...^an=a1^a2^...^ai'^...^an可以得到ai=ai'。所以將ai改變成ai'不是一個合法的移動。證畢。
3、Sprague-Grundy函數
首先給出這個函數適用范圍,貌似所有博弈的題目都是符合這個范圍。現在來研究似乎更為一般的游戲:給定一個有向無環圖和一個其實頂點上的一枚棋子,兩名選手交替將這棋子沿有向邊移動,無法移動者判負。事實上,這個游戲可以認為是所有Impartial Combinatorial Games的抽象模型。即任何一個ICG都可以通過把每個局面看成一個頂點,對每個局面和它的子局面連一條有向邊來抽象成這個“有向圖游戲”。
首先定義mex(minimal excludant)運算,這是施加于一個集合的運算,表示最小的不屬于這個集合的非負整數。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
對于一個給定的有向無環圖,定義關于圖的每個頂點的Sprague-Garundy函數g如下:g(x)=mex{ g(y) | y是x的后繼 }。
來看一下SG函數的性質。首先,所有的terminal position所對應的頂點,也就是沒有出邊的頂點,其SG值為0,因為它的后繼集合是空集。下面為原作圖
(pic1)
對于一個g(x) = 0的頂點x,它的所有后繼 y 都滿足 g(y) != 0。
對于一個g(x) != 0的頂點,必定存在一個后繼 y 滿足 g(y) = 0。
即頂點 x 代表的position為P-position,當且僅當 g(x) = 0。相應地N-position為 g(x) != 0。所以有向無環圖的每個頂點SG值能夠計算出來,就能找到每種局面的必勝策略。但SG函數的用途遠沒有這么簡單。如果將有向圖游戲變復雜,比如,有向圖上不止有一枚棋子,而是有n枚,每次可以人選一顆移動,這時怎么找必勝策略?
?
考慮一下頂點的SG值的意義。當g(x) = k時,表明對于任意一個 0 <= i < k,都存在x的一個后繼 y 滿足g(y) = i。即當某棋子的SG值為k時,我們可以把它變成0、變成1、……、變成k-1,但絕對不能保持k不變。這類似于Nim游戲,Nim游戲的規則就是:每次選擇一堆數量為k的石子,可以把它變成0、變成1、……、變成k-1,但絕對不能保持k不變。這表明,如果將n枚棋子所在的頂點的SG值看作n堆相應數量的石子,那么這個Nim游戲的每個必勝策略都對應于原來這n枚棋子的必勝策略!
對于n個棋子,設它們對應的頂點的SG值分別為(a1,a2,...,an),再設局面(a1,a2,...,an)時的Nim游戲的一種必勝策略是把ai變成k,那么原游戲的一種必勝策略就是把第i枚棋子移動到一個SG值為k的頂點。其實我們還是只要證明這種多棋子的有向圖游戲的局面是P-position當且僅當所有棋子所在的位置的SG函數的異或為0。這個證明與上面的Bouton's Theorem幾乎是完全相同的,只需要適當的改幾個名詞就行了。
剛才,為了使問題看上去更容易一些,認為n枚棋子是在一個有向圖上移動。但如果不是在一個有向圖上,而是每個棋子在其對應的有向圖上,每次可以任選一個棋子(也就是任選一個有向圖)進行移動,這樣也不會給結論帶來任何變化。
?
所以我們可以定義有向圖游戲的和(Sum of Graph Games):設G1、G2、……、Gn是n個有向圖游戲,定義游戲G是G1、G2、……、Gn的和(Sum),游戲G的移動規則是:任選一個子游戲Gi并移動上面的棋子。Sprague-Grundy Theorem就是:g(G)=g(G1)^g(G2)^...^g(Gn)。也就是說,游戲的和的SG函數值是它的所有子游戲的SG函數值的異或。
?
任何一個ICG都可以抽象成一個有向圖游戲。所以“SG函數”和“游戲的和”的概念就不是局限于有向圖游戲。我們給每個ICG的每個position定義SG值,也可以定義n個ICG的和。所以說當我們面對由n個游戲組合成的一個游戲時,只需對于每個游戲找出求它的每個局面的SG值的方法,就可以把這些SG值全部看成Nim的石子堆,然后依照找Nim的必勝策略的方法來找這個游戲的必勝策略了!(Nim其實就是n個從一堆中拿石子的游戲求SG的變型,總SG=n個sg的異或)。(very important)
?
有n堆石子,每次可以從第1堆石子里取1顆、2顆或3顆,可以從第2堆石子里取奇數顆,可以從第3堆及以后石子里取任意顆……我們可以把它看作3個子游戲,第1個子游戲只有一堆石子,每次可以取1、2、3顆,很容易(看下圖pic2)看出x%4==0時處于P局面,即x顆石子的局面的SG值是x%4,(即把pic2中的值改成原值%4)。第2個子游戲也是只有一堆石子,每次可以取奇數顆,經過簡單的畫圖可以知道這個游戲有x顆石子時的SG值是x%2。第3個游戲有n-2堆石子,就是一個Nim游戲。對于原游戲的每個局面,把三個子游戲的SG值異或一下就得到了整個游戲的SG值,然后就可以根據這個SG值判斷是否有必勝策略以及做出決策了。其實看作3個子游戲還是保守了些,干脆看作n個子游戲,其中第1、2個子游戲如上所述,第3個及以后的子游戲都是“1堆石子,每次取幾顆都可以”,稱為“任取石子游戲”,這個超簡單的游戲有x顆石子的SG值顯然就是x。其實,n堆石子的Nim游戲本身不就是n個“任取石子游戲”的和嗎?
(pic2)
所以,對于我們來說,SG函數與“游戲的和”的概念不是讓我們去組合、制造稀奇古怪的游戲,而是把遇到的看上去有些復雜的游戲試圖分成若干個子游戲,對于每個比原游戲簡化很多的子游戲找出它的SG函數,然后全部異或起來就得到了原游戲的SG函數,就可以解決原游戲了。
以下是本文最重要的部分:
解題模型:
1.把原游戲分解成多個獨立的子游戲,則原游戲的SG函數值是它的所有子游戲的SG函數值的異或。
? ? ? ?即sg(G)=sg(G1)^sg(G2)^...^sg(Gn)。
2.分別考慮沒一個子游戲,計算其SG值。
? ? ?SG值的計算方法:(重點)
? ? ??1.可選步數為1~m的連續整數,直接取模即可,SG(x) = x % (m+1);
2.可選步數為任意步,SG(x)?= x;
3.可選步數為一系列不連續的數,用模板計算。
模板1:打表
1 //f[], 可以取走的石頭個數 2 //sg[],SG函數值 3 //mex[],mex{} 4 int f[MAXN], sg[MAXN], mex[MAXN]; 5 void getSG(int n) 6 { 7 int i, j; 8 memset(sg, 0, sizeof(sg)); 9 for(i = 1;i <= n;++i) 10 { 11 memset(mex, 0, sizeof(mex)); 12 for(j = 1;f[j] <= i;++j) 13 mex[sg[i - f[j]] = 1; 14 for(j = 0;j <= n;++j) 15 if(mex[j] == 0) 16 { 17 sg[i] = j; 18 break; 19 } 20 } 21 }模板2:DFS
//s[i] 數組要從小排到大,SG函數要初始化為 -1, 每個集合只需初始化一遍 //n是集合s 的大小,s[i]是定義的特殊取法規則的數組 int s[105], sg[10005], n; //memset(sg, -1, sizeof(sg)); //sort(s, s + n); int SG_dfs(int x) {int i;if(sg[x] != -1)return sg[x];bool vis[105];memset(vis, 0, sizeof(vis));for(i = 0;i < n;++i){if(x >= s[i]){SG_dfs(x - s[i]);vis[sg[x - s[i]] = 1;//mex{} }}int e;for(i = 0;;++i)if(!vis[i]){e = i;break;}return sg[x] = e; }首選打表,實在不行才用DFS。
3.計算sg(G)=sg(G1)^sg(G2)^...^sg(Gn),sg(G)=0,即P-Position,即先手必敗。
?
--------------------------------------------------------------------------------------------
原博主下面還有題,另外也開了一篇寫題的博客
轉載于:https://www.cnblogs.com/shuizhidao/p/9271712.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的(转)博弈 SG函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为快应用引擎架构及开发实践
- 下一篇: [BZOJ1833][ZJOI2010]