博弈论之SG函数
慣例先引入一些概念:(概念來自于網絡)
有向圖游戲:
給定一個有向無環圖,圖中有一個唯一的起點,在起點上放有一枚棋子。兩名玩家交替地把這枚棋子沿有向邊進行移動,每次可以移動一步,無法移動者判負。該游戲被稱為有向圖游戲。
任何一個公平組合游戲都可以轉化為有向圖游戲。具體方法是,把每個局面看成圖中的一個節點,并且從每個局面向沿著合法行動能夠到達的下一個局面連有向邊。
Mex運算: 設S表示一個非負整數集合。定義mex(S)為求出不屬于集合S的最小非負整數的運算,即: mex(S) = min{x},
x屬于自然數,且x不屬于S。
SG函數: 在有向圖游戲中,對于每個節點x,設從x出發共有k條有向邊,分別到達節點y1, y2, …,
yk,定義SG(x)為x的后繼節點y1, y2, …, yk 的SG函數值構成的集合再執行mex(S)運算的結果,即: SG(x) =
mex({SG(y1), SG(y2), …, SG(yk)})
特別地,整個有向圖游戲G的SG函數值被定義為有向圖游戲起點s的SG函數值,即SG(G) = SG(s)。
定理:
有向圖游戲的某個局面必勝,當且僅當該局面對應節點的SG函數值大于0。
有向圖游戲的某個局面必敗,當且僅當該局面對應節點的SG函數值等于0。
用圖片舉個例子
各個結點表示一種局面,紅色的數字表示該局面的SG函數的值,具體的數值大小可以由Mex函數得到,當先手面臨SG函數值為0的結點時必敗,否則必勝,因為只要結點值不為0就一定存在下一步可以走到結點值為0的結點。(只有存在下一步結點值為0的情況當前結點才不為0)。
來道例題理解:
給定n堆石子以及一個由k個不同正整數構成的數字集合S。
現在有兩位玩家輪流操作,每次操作可以從任意一堆石子中拿取石子,每次拿取的石子數量必須包含于集合S,最后無法進行操作的人視為失敗。
問如果兩人都采用最優策略,先手是否必勝。
輸入格式
第一行包含整數k,表示數字集合S中數字的個數。
第二行包含k個整數,其中第i個整數表示數字集合S中的第i個數si。
第三行包含整數n。
第四行包含n個整數,其中第i個整數表示第i堆石子的數量hi。
輸出格式
如果先手方必勝,則輸出“Yes”。
否則,輸出“No”。
數據范圍
1≤n,k≤1001≤n,k≤100,
1≤si,hi≤10000
輸入樣例:
2
2 5
3
2 4 7
輸出案例
Yes
大意:給定n堆石子,每次只能取任意堆石子里的指定個數,問先手必勝還是必敗。
此時題目并不適應于之前的Nim問題模型,而由于每堆石子都有k種拿法,總共的拿取步驟將會是指數級別,所以這時需要將每堆石子的操作分別轉化為SG函數模型,只要每堆石子的初始數量的SG函數值的異或和不為0即為必勝,否則必敗。(原因同Nim游戲)
代碼:(關鍵在于SG函數的寫法)
#include<bits/stdc++.h> using namespace std;int n,m,x; int s[110],f[110];int sg(int x) {if(f[x] != -1)//記憶化搜索,保證每個結點只被搜索一次return f[x];unordered_set<int> mp;//每進入一個新的結點,set要被重置,因為結點的值只與該節點的下一步有關for(int i = 0; i < m; i ++ ){int sum = x - s[i];if(sum >= 0)//如果存在下一步的走法mp.insert(sg(sum));//哈希表存下一步的SG函數值}for(int i = 0; ; i ++ ){if(!mp.count(i))//Mex函數,當前結點的SG函數值為下一步結點函數值中不存在的最小自然數return f[x] = i;} } int main() {int res = 0;memset(f,-1,sizeof(f));cin>>m;for(int i = 0; i < m; i ++ )cin>>s[i];cin>>n;while(n -- ){cin>>x;res ^= sg(x);}if(res)cout<<"Yes";elsecout<<"No";return 0; }總結
- 上一篇: X264码率控制流程分析
- 下一篇: Django里面是文件静态化的方法