公平组合游戏:
1.由两名玩家交替行动;
2.在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关;
3.不能行动的玩家判负;

有向图游戏:给定一个有向无环图,图中有一个唯一的起点,在起点上放有一枚棋子。两名玩家交替地把这枚棋子沿有向边进行移动,每次可以移动一步,无法移动者判负。
任何一个公平组合游戏都可以转化为有向图游戏。具体方法是,把每个局面看成图中的一个节点,并且从每个局面向沿着合法行动能够到达的下一个局面连有向边。
\(nim\) 游戏属于公平组合游戏。

在有向图游戏中,对于每个节点 \(x\),设从 \(x\) 出发共有 \(k\) 条有向边,分别到达节点 \(y_1, y_2, ..., y_k\),定义 \(SG(x)\)\(x\) 的后继节点 \(y_1, y_2, ..., y_k\)\(SG\) 函数值构成的集合再执行 \(mex(S)\) 运算的结果。
即:\(SG(x) = mex({SG(y_1), SG(y_2), ..., SG(y_k)})\)

nim游戏

lugou:https://www.luogu.com.cn/problem/P2197
acwing:https://www.acwing.com/problem/content/893/

\(n\) 堆石子,两人依次从任意一堆中取任意个,不能操作的人输。问先手是否必胜。
\(a_1\) ^ \(a_2\) ^ ... ^ \(a_n\) != 0 时,先手必胜。

#include <bits/stdc++.h>
using namespace std;
int T, n;
void solve(){
	cin >> n;
	int k = 0, x;
	for (int i = 1; i <= n; i ++ ){
		cin >> x;
		k ^= x;
	}
	if (k) cout << "Yes\n";
	else cout << "No\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> T;
	while ( T -- )
		solve();
	return 0;
}

SG函数

\(SG\) 函数异或值大于 0 则先手必胜。

acwing:https://www.acwing.com/problem/content/895/

\(n\) 堆石子以及一个 \(k\) 个不同整数组成的数字集合 \(S\)。两个玩家依次操作,每次从任意一堆石子中取集合中一个数个数的石子,不能操作的人输。问先手是否必胜。

#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = 1e4 + 10;
int n, m, x, ans, s[N], f[M];
int sg(int x){
	if (f[x] != -1) return f[x];
	unordered_set <int> S;
	for (int i = 0; i < m; i ++ ){
		int sum = s[i];
		if (x >= sum) S.insert(sg(x - sum));
	}
	for (int i = 0; ; i ++ )
		if (!S.count(i))
			return f[x] = i;
}
int main(){
	cin >> m;
	for (int i = 0; i < m; i ++ )
		cin >> s[i];
	memset(f, -1, sizeof f);
	cin >> n;
	for (int i = 0; i < n; i ++ ){
		cin >> x;
		ans ^= sg(x);
	}
	if (ans) cout << "Yes\n";
	else cout << "No\n";
	return 0;
}

acwing:https://www.acwing.com/problem/content/896/

\(n\) 堆石子,两个玩家依次取走一堆石子,再放入两堆数量更小的石子,不能操作的人输。问先手是否必胜。

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int ans, n, f[N], x;
int sg(int x){
	if (f[x] != -1) return f[x];
	unordered_set<int> S;
	for (int i = 0; i < x; i ++ )
		for (int j = 0; j <= i; j ++ )
			S.insert(sg(i) ^ sg(j));
	for (int i = 0; ; i ++ )
		if (!S.count(i))
			return f[x] = i;
}
int main(){
	cin >> n;
	memset(f, -1, sizeof f);
	for (int i = 0; i < n; i ++ ){
		cin >> x;
		ans ^= sg(x);
	}
	if (ans) cout << "Yes\n";
	else cout << "No\n";
	return 0;
}
posted on 2022-05-03 22:12  Hamine  阅读(337)  评论(0编辑  收藏  举报