SG函数详解
在之前的博客中我主要介紹了幾種常見的博弈論,那些都比較固定,當我們遇到一些新型的博弈問題時,未必可以用之前的模型去解決他,現在我就來介紹一種工具來解決規則比較奇怪的博弈游戲。
先來說一下可以用SG函數解決的問題所需要滿足的條件:
(1)游戲人數為兩人
(2)兩人交替進行某種游戲規定的操作,每次操作,選手都可以在當前合法的操作中選取一種
(3)對于游戲的任意一種可能的局面,合法的操作集合只取決于這個局面的本身,而與操作者無關
就拿巴什博弈舉例,每個操作者每次都可以拿走1~m塊石頭,每次拿走石頭的個數取決于當前還剩多少塊石頭,而與之前的操作無關,這就是一個可以利用SG函數來解決的問題。
下面來對P-position(必敗點)和N-position(必勝點)來作下解釋:
處在必勝點的選手如果不出現決策上的失誤,則選手是必勝的,同理,處在必敗點的選手是必敗的。
依舊拿巴什博弈舉例,假如一開始給定m+2塊石頭,每次可以取1~m塊石頭,則先手必勝,所以m+2也就是一個必勝點,若一開始給定m+1塊石頭,則無論先手第一次取幾塊石頭,后手都可以一次性取完剩余的石頭,所以m+1是一個必敗點。
必敗點和必勝點滿足的性質:
(1)終結點是必敗點
(2)可以進行一次操作到達必敗點的為必勝點
(3)從必敗點只能到達必勝點
介紹SG函數前先介紹一下mex函數,表示最小的不屬于這個集合的非負整數,比如mex{0,1,3,4}=2,mex{1,2,3}=0;
任何一個可以用SG函數解決的問題都可以抽象成一個有向圖游戲,SG函數是用于為這種有向圖游戲提供最優策略的。(SG(x)=0代表x為必敗態,SG(x)!=0代表x為必勝態)
對于給定游戲規則下的有向無環圖,定義SG(x)=mex{SG(y) | y是x的后繼}
容易知道終止狀態的SG值一定是0,對于一個x滿足SG(x)=0,則對于x的所有后繼(經過一次操作可到達的狀態)y一定有SG(y)!=0,類似的,對于一個x滿足SG(x)!=0,則對于x的所有后繼y均有SG(y)==0。
頂點SG值的意義:
當SG(x)=k時,表明對于任何一個0<=i<k,都存在x的一個后繼y滿足SG(y)=i。
下面給出常見博弈論的解題方法:
把原問題按照游戲規則的差別分解成多個獨立的子游戲,并計算每個子游戲下的SG值,最后求出所有子游戲的SG值的異或
最后給出SG值的計算方法:
(1)若可選步數為1~m的連續整數(巴什博弈),則SG(x)=x%(m+1)
(2)若可選步數為任意步,則SG(x)=x
(3)剩余的情況只能具體問題具體分析了,下面給出求SG的函數模板
打表計算SG值
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<queue> using namespace std; const int N=1e3+10;//具體數據 int f[N],sg[N],vis[N]; //f[]記錄可以進行的操作(比如取走多少個石子) //sg[]記錄每種狀態的sg值 //vis[x]用于標記出現過的x的后繼的sg值 void SG(int n) {memset(sg,0,sizeof sg);//0是終止狀態(必敗態),所以sg[0]一定為0 for(int i=1;i<=n;i++){memset(vis,0,sizeof vis);for(int j=1;f[j]<=i;j++)vis[sg[i-f[j]]]=1;//i-f[j]是狀態i進行一次操作可以到達的狀態 for(int j=0;j<=n;j++)//求解i的后繼的sg值中未出現的最小自然數 {if(!vis[j]){sg[i]=j;break;}} } }dfs計算SG值
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<queue> using namespace std; const int N=1e3+10;//具體數據 int f[N],sg[N],vis[N]; //f[]記錄可以進行的操作(比如取走多少個石子) //sg[]記錄每種狀態的sg值 //vis[x]用于標記出現過的x的后繼的sg值 int SG(int x) {//因為sg值可能取到1,所以應該初始化sg數組為-1 if(sg[x]!=-1) return sg[x];memset(vis,0,sizeof vis);for(int i=0;i<n;i++){if(x>=f[i]){SG(x-f[i]);vis[sg[x-f[i]]]=1;}}int e;for(int i=0;;i++){if(!vis[i]){e=i;break;}}return sg[x]=e; }總結
- 上一篇: c++中的多线程
- 下一篇: Oracle sqluldr2