SG 函数 博弈问题

前言:

如果你没有了解过博弈问题,你可以先点击 这里 学习一下,讲的非常好,我只能orz,这里我讲出我的理解。

我的理解:

定义N为先手必胜,P为先手必败。

icg博弈可以转换成有向无环图

游戏开始时有一颗放在起点的棋子,

两个玩家轮流移动棋子,

直到不能移动的玩家落败。

所有只能移动到终点的局面都是必胜的,

就是N下面至少有一个P

而P下面全部都是N

即所有只能连接必胜点的点是必败的。

我们用递归的思路可以计算出所有点的状态。

因为遍历搜素所有状态太耗时了,于是出现了sg函数(魔法一般的函数)
定义p点是零级胜态,和 p直接相连的点是一级胜态点,可以进入一级胜态点和零级胜态点的点是二级胜态点。以此类推,n级胜态点和n-1级胜态点都相连。
两个同级的胜态组合是败态,两个不同级的胜态组合是胜态,因为后手可以拷贝先手的操作,直到无法继续操作,游戏结束,所以,两个同级的胜态组合是败态。而两个不同的胜态,先手可以将其中一个转化成和另一个一样,从而让后手面临两个一样的胜态,所以不同的胜态组合是胜态。
SG函数能够用来将一个状态映射成一个非负整数的值
SG(A)=mex(SG(B)|A->B)
也就是说,SG函数的值就是表示当前状态是几级胜态,比如SG(A)=1,那么A就是一级胜态
因为两个同级的胜态组合是败态,两个不同级的胜态组合是胜态,所以SG函数异或之后如果为零,那么就是必败,否则就是必胜
总结 就是他定义了胜态的概念,然后牢记同级胜态组合必败,不同级胜态组合必胜,
最后值得注意的是 多组石头的胜态是每组石头的胜态的异或和。 其实就是上面的原理。 

相应题目

 

 

ac代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
const int N=20;
int f[N],sg[N],s[N];
int gcd(int a,int b){
    if(b==0)return a;
    return gcd(b,a%b);
}
void getf(int k){
    for(int i=1;i<=k;i++){
        if(gcd(i,k)==1)f[i]=1;
        else f[i]=0;
    }
}
void getSG(int n){
    int i,j;
    memset(sg,0,sizeof(sg));
    for(i=1;i<=n;i++){//求对于i的sg 
        memset(s,0,sizeof(s));
        getf(i);
        for(j=1;j<=i;j++){
            if(f[j]){
                s[sg[i-j]]=1;
            }
        }
        for( j=1;j<=i/2;j++){
            s[sg[j]^sg[i-j]]=1;
        }
        for(j=0;;j++)if(s[j]!=1){
            sg[i]=j;
            break;
        }
    }
}
int main(){
    getSG(15);
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        if(sg[n]!=0){
            printf("Teacher Ma\n");
        }
        else{
            printf("Xiao Huo Zi\n");
        }
    }
}

 

posted @ 2020-12-07 00:06  Rain_luo  阅读(46)  评论(0编辑  收藏  举报