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