博弈基础小结

巴什博弈

一堆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];
}
posted @ 2019-05-04 21:37  fishers  阅读(228)  评论(2编辑  收藏  举报