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
#define exp 1e-8
#define mian main
#define pii pair
#define pll pair
#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
#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
#define exp 1e-8
#define mian main
#define pii pair
#define pll pair
#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
#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=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;
}

 

 

你可能感兴趣的:(博弈)