博弈论(1)SG定理

用于把多堆问题简化成每个单堆问题的异或和

 

Sprague-Grundy定理(SG定理):

        游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。

SG函数:

        首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

        对于任意状态 x , 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 这样 集合S 的终态必然是空集,所以SG函数的终态为 SG(x) = 0,当且仅当 x 为必败点P时。

  即,该状态的SG值 = mex{ 后继状态的SG值 }

 

http://acm.hdu.edu.cn/showproblem.php?pid=3980

 

用SG数组记录每个状态的SG值,f数组用来进行mex{}的计算

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<vector>
#include<iostream> 
#define MAXN 1000005
#define PI acos(-1.0)
using namespace std;
typedef long long ll;
int SG[1005],f[1005];
int getsg(int n,int m)
{
	SG[0]=0;
	for(int i=1;i<=n;i++)//从1开始,逆推出n的SG值 
	{	SG[i]=0;
		if(i<m) continue;
		memset(f,0,sizeof(f));
		for(int j=m;j<=i;j++)//枚举后继状态 
		{
			
			f[SG[j-m]^SG[i-j]]=1;
			
		}
		for(int k=0;;k++)//mex计算 
		{
			if(!f[k])
			{
			SG[i]=k;
			
			break;	
			}
		}
		
	}
	return SG[n];
}
int main()
{	int T,d=1;
	scanf("%d",&T);
	while(T--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		if(n<m) {
			printf("Case #%d: abcdxyzk
",d++);
			continue;
		}
		n-=m;
		int x=getsg(n,m);
		if(x) printf("Case #%d: abcdxyzk
",d++);
		else printf("Case #%d: aekdycoin
",d++);
	}
	
	
	
	
	return 0;
} 

  

 

原文地址:https://www.cnblogs.com/lnu161403214/p/8665577.html