初始博弈【三】Grundy数

n堆的Nim


这块知识点开始是懵的…

通过看《挑战程序》和一个可爱小学妹的博客学习了很多^_^

http://blog.csdn.net/mikasa3/article/details/51366884


在这里引入Grundy的概念

int grundy(int x){
	集合S={};
	for(j=1:k){
		if(a_j<=x) 将grundy(x-a_j)加到S集合中 
	} 
	return 最小的不属于S的非负整数 
}


Grundy值:除(任意一步所能转移到 的状态  的Grundy值 )以外的最小非负整数,这样的Grundy值,和Nim中的一个石子堆类似,有如下性质:

mex{0,1,2}=3;mex{ 1, 2}=0 ; mex{ 2, 3}=1

1.Nim中有x颗石子的石子堆,能转移成有0,1,……,x-1堆石子的石子堆

2.从Grundy值为x的状态出发,可以转移到Grundy值为0,1,……,x-1的状态。 


Nim:

所有石子堆的石子数xi的XOR

x1 XOR x2 XOR …XOR xk

为0必败,为1必胜


Grundy值等价于Nim中石子数,所以对于Grundy的情况:

所有硬币堆的Grundy的值

grundy(x1)  XOR  grundy(x2)  XOR …… XOR grundy(xn)

 为0必败,为1必胜。


#include <cstdio>
#include <set>
#include <algorithm>
#define maxn 105
#define maxk 105
using namespace std;
int N,K,X[maxn],A[maxk];
int grundy[maxn+1];


void solve(){
	//轮到自己剩0的时候必败 
	grundy[0]=0;			
	
	//计算grundy 
	int max_x=*max_element(X,X+N);
	for(int j=1;j<=max_x;j++){
		set<int> s;				//存储当前的状态 
		for(int i=0;i<K;i++){
			if(A[i]<=j) s.insert(grundy[j-A[i]]);
		}
		int g=0;				//寻找当前状态的最小排斥 
		while(s.count(g)!=0) g++;
		grundy[j]=g;
	}	
	int ans=0;
	for(int i=0;i<N;i++) ans^grundy[X[i]];
	
	if(ans!=0)	puts("Alice");
	else puts("Bob");
	
}


int main(){
	scanf("%d%d",&N,&K);	
	for(int i=0;i<N;i++){
		scanf("%d",&A[i]);
	}
	for(int j=0;j<K;j++){
		scanf("%d",&X[j]);
	}
	solve();
	
	return 0;
} 



poj2311






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值