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