初始博弈【三】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






發佈了69 篇原創文章 · 獲贊 49 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章