一、必敗點和必勝點
必敗點: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;
}