SG函数 与 ICG问题

ICG##

ICG(Impartial Combinatorial Games)游戏是组合游戏(Combinatorial Games)的一类
满足如下性质:
①有两名玩家
②两名玩家轮流操作,在一个有限集合内任选一个进行操作,改变游戏当前局面
③一个局面的合法操作,只取决于游戏局面本身且固定存在,与玩家次序或者任何其它因素无关
④无法操作者,即操作集合为空,输掉游戏,另一方获胜

Nim游戏##

Nim游戏是经典的ICG游戏,也是组合游戏的一个重要模型,非常的经典
对于n堆石子,两名玩家轮流取走其中一堆中的若干石子,不能取走者拜

结论:我们将石子数异或起来,为0则先手必败,否则先手必胜
这样的结果也成为Nim和

SG函数##

普通的ICG游戏可能就没有Nim游戏来的那么直接,可能会加上若干限制条件,这个时候就需要动用SG函数了

我们将游戏局面和操作抽象为一个DAG图
我们定义一个局面x的\(SG(x) = mex(SG(y))\)
其中y是x可直达的点,\(mex\)指取未出现的最小的非负整数
例如\(SG(x) = mex(0,1,3) = 2\)

显然终止状态的SG函数为0

\(SG函数的性质\)

SG为0的状态为必败态
如果一个局面SG函数非0,说明其一定可以到达SG为0的状态,所以必胜
如果一个局面的SG函数为0,说明其要么是终止态,可达的局面均非0,必败

\(SG函数解决ICG游戏\)

思考SG函数的定义,如果一个局面\(SG(x) = k\),意味着其下一步一定会到达一个小于k的局面,而且任意小于k的局面都可达
像不像取石子?没错

若ICG游戏分成若干个独立的游戏,我们求出每个游戏的SG函数并求出Nim和,就可以判断局面的胜败

SG函数的求法:
如果局面集合不大,可以搜索
如果局面集合很大,就要通过打表找规律数学推导找到普遍规律,进而加速SG函数的求解

BZOJ1228为例
经打表找规律后,可以发现
SG(a,b) =
①a,b为奇,0
②a,b为偶,SG(a/2,b/2) + 1
③否则设b为奇数,SG(a,b + 1)

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 100005,maxm = 100005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
	return out * flag;
}
int sg(int x,int y){
	int re = 0;
	while (true){
		if ((x & 1) && (y & 1)) return re;
		else if (!(x & 1) && !(y & 1)) re++,x >>= 1,y >>= 1;
		else if (x & 1) x++;
		else y++;
	}
	return re;
}
int main(){
	int T = read();
	while (T--){
		int n = read(),ans = 0; n >>= 1;
		for (int i = 1; i <= n; i++)
			ans ^= sg(read(),read());
		if (ans) puts("YES");
		else puts("NO");
	}
	return 0;
}

posted @ 2018-02-27 13:48  Mychael  阅读(282)  评论(0编辑  收藏  举报