大一暑期的时候接触过一点组合博弈,一年之后也基本忘光了,前两天从新找了不少资料温习了一遍。记录下我对组合博弈(sg函数)的理解:
一)sg函数
sg [x] = mex{ y | y是 x 的所有的可能直接后继}
或:
sg [u] = mex{ v | 图中有一条u指向v的边}。
mex{}是定义在整数集合上的操作。它的自变量是任意整数集合,函数值是不属于该集合的最小自然数。
很多资料上都只是写:sg [x] = mex{ y | y是 x 的一个后继},刚学的时候我总怀疑 y是x的直接后继,还是y是x的所有可能后继?,实际上应该是后者,y准确的来说也应该被描述为一个集合:{ y | y是x所有的可能直接后继}。我没有直接在论文或博客中看到这句话,但按照我的推理应该是这样的。
比如:
我们都知道,有n堆石子a1, a2, a3....an,每次都可以拿任意个,只能从一堆里拿,不能不拿。这个游戏sg值就等于ai。即第i队石子的sg[ i ]=ai。
按照 sg [x] = mex{ y | y是 x 的所有的可能直接后继},因为每次都可以从第 i 堆里拿任意个石子,所以可以将ai变成 0,1,2,3.....ai-1 任意一个数字。即集合 y={0,1,2......ai-1}所以 sg[ ai ]=a[ i ]。
再看到我大一时留下的sg函数模版:
<span style="font-size:12px;">void sprague_grundy()
{
int i,j;
sg[0]=0;
for (i=1;i<H;i++){ //H为石头的堆数,k为s集合元素的个数
memset(mex,0,sizeof(mex));
j=1;
while (j<=k && i>=s[j]){
mex[sg[i-s[j]]]=1; //s[]为题目要求的只能取的个数
j++;
}
j=0;
while (mex[j]) j++;
sg[i]=j;
}
} </span>
while循环里显然是把a[i]的所有可能后继都标记了。sg [x] = mex{ y | y是 x 的所有的可能直接后继},应该是无疑的了。
注:初学的同学maybe 会对 “y 是 x 的所有的可能直接后继”仍能不理解。
如图:
y1,y2,y3 是 x 的所有直接后继,z 是y2的直接后继,但是z不是x的直接后继
二)组合
<span style="font-size:12px;">/* ***********************************************
Author :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define mst(a,k) memset(a,k,sizeof(a));
#define LL long long
#define maxn 100005
#define mod 100000007
using namespace std;
int sg[(1<<20)+10];
void init()
{
for(int i=1; i<(1<<20); i++) //枚举一行棋盘的所有状态
{
int mex[25];
memset(mex,-1,sizeof(mex)); //每个新状态mex函数都不一样
int last=-1;
for(int j=0; j<20; j++) //向右走,状态i的所有可能后继状态给它标记了
{
if(!(i&(1<<j))) //找到右边第一个为空的格子
last=j;
if((i&(1<<j))) //找到第一个棋子
{
if(last!=-1){
mex[sg[i^(1<<j)^(1<<last)]]=1; //标记移动后的后继状态
}
}
}
int j=0;
while(mex[j]!=-1) j++;
sg[i]=j;
}
}
int main()
{
init();
int t,n,m,x;
scan(t);
while(t--)
{
scan(n);
int ans=0;
for(int i=1; i<=n; i++)
{
scan(m);
int t=0;
for(int i=1; i<=m; i++)
{
scan(x);
t^=1<<(20-x);
}
ans+=sg[t];
}
if(ans)
{
puts("YES");
}
else
{
puts("NO");
}
}
}</span><span style="font-size:14px;">
</span>