SG函數

一、必敗點和必勝點

必敗點:P點,處於這一點時在雙方操作均正確的前提下必敗

必勝點:N點,處於這一點時雙方操作均正確的前提下必勝。

有關的性質:

1、所有終結點都是必敗點

2、必敗點P無論怎麼操作只能進入必勝點N

3、至少有一種操作使可以從必勝點N到達必敗點P

Sprague-Grundy定理(SG定理):

        遊戲和的SG函數等於各個遊戲SG函數的Nim和。Bouton定理就是Sprague-Grundy定理在Nim遊戲中的直接應用,因爲單堆的Nim遊戲 SG函數滿足 SG(x) = x。所以多堆的情況下,就是各個堆的SG值異或起來

mex(minimal excludant)運算

這是一個集合的運算,結果是不屬於這個集合中的最小的非負整數。

例如mex{0,1,2,3}=4

mex{1,3,5}=0;

 

而SG[X]的含義就是,當前在x的狀態下(一般可以看做有x個石子),可以取f[i]個(f數組用來記錄可以怎麼取),得到的結果的sg值進行mex運算,例如:

有1堆n個石子,一次可以取走1或3個,那麼sg函數是多少?

sg[0]=0,f[1]=1,f[2]=3,

x=1時,可以取走1個,那麼sg[1]={1-f[1]}={sg[0]}=1;

x=2時,可以取走1個,sg[2]={2-f[1]}={sg[1]}=0;

x=3時,可以取走1或3個,sg[3]={3-f[1],3-f[2]}={ sg[2], sg[0]}=1

......

所以可以得到求sg函數的步驟:

(1)用f數組將當前可以取走的石子數記錄

(2)在用另一個數組s記錄當前狀態取完後的後繼狀態

(3)對這些後繼狀態進行mex運算

(4)不斷重複求所有狀態(1-n)即得到sg函數

 

求SG函數

(1)打表求,以hdu1848爲例

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int N=1e5+10;
int f[N],sg[N],s[N],n,m,p;//f[N]爲可改變當前狀態的方式,N爲方式數  s[i]爲x的後繼狀態的集合
void SG(int n)
{
    met(sg,0);//sg[0]始終=0
    for(int i=1;i<=n;i++){
        met(s,0);
        for(int j=1;f[j]<=i;j++)
            s[sg[i-f[j]]]=1;
        for(int j=0;j<=n;j++)
            if(s[j]==0){
                sg[i]=j;
                break;
            }
    }
}
int main()
{
    f[1]=1;
    f[2]=1;
    for(int i=3;i<=17;i++)// f數組記錄可以拿的石子數,即斐波那契數
        f[i]=f[i-1]+f[i-2];
    SG(1010);
    while(scanf("%d%d%d",&m,&n,&p)&&(m||n||p)){
        if(sg[m]^sg[n]^sg[p])
            printf("Fibo\n");
        else printf("Nacci\n");
    }
}

(2)dfs求

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
//注意 f數組要按從小到大排序 SG函數要初始化爲-1 對於每個集合只需初始化1遍
//n是集合f的大小 f[i]是定義的特殊取法規則的數組
int f[110],sg[10010],n;
int SG_dfs(int x)
{
    int i;
    if(sg[x]!=-1)
        return sg[x];
    bool s[110];
    memset(s,0,sizeof(s));
    for(i=0;i<n;i++)
    {
        if(x>=f[i])
        {
            SG_dfs(x-f[i]);
            s[sg[x-f[i]]]=1;
        }
    }
    int e;
    for(i=0;;i++)
        if(!s[i])
        {
            e=i;
            break;
        }
     sg[x]=e;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章