博弈基础小结

巴什博弈

一堆n个物品,两个人轮流取1~m个,最后一个取光的人胜利

if(n%(m+1)) return false;
else return true;

wythoff博弈

两堆各若干个物品,两人轮流从一堆中取走至少一个,或者从两堆中取走相同数量的物品,最后一个取光的人胜利

差值 * 黄金分割比 == 最小值时后手赢,否则先手赢

double r = (sqrt(5.0)+1) / 2;
int d = abs(a-b) * r;
if( d != min(a,b) ) return true;
else return false;

尼姆博弈

n堆物品,两人轮流取至少一个,最后一个取光的人胜利

int res = 0;
for(int i=1;i<=n;i++){
    res = res ^ a[i];
}
if(res) return true;
else return false;

SG函数

hdu1848,斐波那契堆

#include <iostream>
#include <string.h>
#define MAXN 3010
using namespace std;

int f[MAXN], sg[MAXN];

void init(){//得到1000以内的fibonacci数列
    f[1]=1;
    f[2]=2;
    for(int i=3; ; i++){
        f[i]=f[i-1]+f[i-2];
        if(f[i]>1000){
            break;
        }
    }
}

void get_sg(){//sg函数打表
    for(int i=1; i<=1000; i++){
        int vis[MAXN];
        memset(vis, 0, sizeof(vis));
        for(int j=1; f[j]<=i; j++){//找到当前节点能到达的点
            vis[sg[i-f[j]]]=1;
        }
        for(int j=0; j<=1000; j++){//求mex函数
            if(!vis[j]){
                sg[i]=j;
                break;
            }
        }
    }
}

int main(void){
    init();
    get_sg();
    int n, m, p;
    while(cin >> n >> m >> p){
        if(n+m+p==0){
            break;
        }
        if(sg[n]^sg[m]^sg[p]){//sg函数性质
            cout << "Fibo" << endl;
        }else{
            cout << "Nacci" << endl;
        }
    }
    return 0;
}

hdu1847,二的幂次

#include<bits/stdc++.h>
using namespace std;


const int maxn = 1010;
int a[20];
int sg[maxn+5];
int n;
int vis[maxn+5];

//sg[1] == 0 //必胜局
//sg[2] == 0 //必胜局 

void getSg(){
    memset(sg,0,sizeof(sg));
    for(int i=1;i<=maxn;i++){
        //寻找每一个i的sg值 
        memset(vis,0,sizeof(vis));
        for(int j=0;j<11;j++){ //枚举可以拿的数量("能拿"说明比当前拥有的数量i小) 
            int temp = i - a[j];
            if(temp < 0 )break;
            vis[sg[temp]] = 1;//可以由temp这个局面推出来
        }
        for(int j=0;j<maxn;j++){
            if(vis[j] == 0){
                sg[i] = j;//sg值为 mex集合中最小的值 
                break;
            }
        }
    }
}


int main(){
    a[0] = 1;
    for(int i=1;i<11;i++){
        a[i] = a[i-1]*2;//可以拿的数量  打表 
    }
    getSg();
    int n;
    while(cin>>n){
        if(sg[n]) puts("Kiki");
        else puts("Cici");
    }

    return 0;
} 

记忆化搜索的递归写法:

memset(sg,-1,sizeof(sg)); 
int mex(int x){
    if(sg[x] != -1) return sg[x];
    bool vis[1200];
    memset(vis,false,sizeof(vis));
    for(int i=0;i<11;i++){
        int temp = x - a[i];
        if(temp < 0) break;
        sg[temp] = mex(temp); //mex:mex操作的含义找到集合中最小的元素 
        vis[sg[temp]] = true; //标记为集合中有的 
    }
    for(int i=0;;i++){
        if(!vis[i]){
            sg[x] = i;
            break;
        }
    }
    return sg[x];
}
上一篇:SP6779 GSS7 - Can you answer these queries VII


下一篇:SG函数