博弈论
博弈论基础
Nim 游戏
地上有 \(n\) 堆石子(每堆石子数量小于 \(10^4\)),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 \(n\) 堆石子的数量,是否存在先手必胜的策略。
把每个状态转化为有向无环图上的一个节点,向它的后继状态连边,可以得到一个博弈图。
\(\text{SG}\) 函数
定义 \(\text{mex}(S)\) 为不属于集合 \(S\) 的最小非负整数,即
对于状态 \(x\) 和它的所有 \(k\) 个后继状态 \(y_1,y_2,\ldots,y_k\),定义 \(\text{SG}\) 函数:
\(\text{SG}\) 定理
对于由 \(n\) 个有向图游戏组成的组合游戏,设它们的起点分别为 \(s_1,s_2,\ldots,s_n\),则有:当且仅当 \(\text{SG}(s_1)\oplus\text{SG}(s_2)\oplus\ldots\oplus\text{SG}(s_n)\neq0\) 时,这个游戏是先手必胜的。同时,这是这一个组合游戏的游戏状态 \(x\) 的 \(\text{SG}\) 函数值。
证明较为复杂,详情可以查看 oi-wiki。
回到题目,对于每堆石子可以转化为一个普通的取石子游戏。
于是可以得到大小为 \(n\) 的石子的 \(\text{SG}\) 函数值为 \(n\),所以只需要判断石子数量的异或和即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 5;
int T, n, x;
signed main(){
cin.tie(0)->sync_with_stdio(0);
cin >> T;
while (T--){
cin >> n;
int ans = 0;
for (int i = 1; i <= n; ++i){
cin >> x;
ans ^= x;
}
cout << (ans ? "Yes" : "No") << "\n";
}
return 0;
}
阶梯取石子游戏
有 \(n\) 堆石头,依次放在阶梯上,甲和乙轮流操作。每次从任意一堆取出至少一个石头,放到下一个阶梯,如果在第 \(1\) 层则将石头扔掉,不能取的输。问谁会获胜。\(n\le10^5\)
类似地,只需保留奇数层的石子,偶数层的石子看作被扔掉,因为一个人将其移动到
奇数层后,另一个人可以又将其移动到偶数层。
于是与普通 Nim 游戏相同。
Bash 博弈
有 \(n\) 堆石头,甲和乙轮流取。每次从任意一堆取出至少一个石头,至多 \(m\) 个,不能取的输。问谁会获胜。\(n\le10^5\)。
(以下叙述均为对于一堆石子)首先 \(\text{SG}(0)=0\),因为若当前石子数为 \(0\),则先手必败。于是可以得到 \(\text{SG}(1)=\text{SG}(2)=\ldots=\text{SG}(m)=1\),因为 \(1\sim m\) 的状态都可以转化为 \(0\) 的状态,是对手面临 \(0\) 的状态,所以这些状态都为先手必胜。于是又可以得到 \(\text{SG}(m+1)=0\),因为 \(m+1\) 的状态只能转移为 \(1\sim m\) 的状态,让对手面临先手必胜状态,此时,己方必败。后续 \(\text{SG}\) 都可以由定义得到:\(\text{SG}(m+2)=2\)……
可以发现,大小为 \(n\) 的石子堆的 \(\text{SG}\) 函数值为 \(n\bmod(m+1)\),可以转化为普通 Nim 游戏进行求解。
Game on Tree
有一棵 \(N\) 个节点的树,节点标号为 \(1,2,⋯,N\),边用 \((x_i,y_i)\) 表示。Alice 和 Bob 在这棵树上玩一个游戏,Alice 先手,两人轮流操作:
选择一条树上存在的边,把它断开使树变成两个连通块。然后把不包含 \(1\) 号点的联通块删除
当一个玩家不能操作时输,你需要算出:假如两人都按最优策略操作,谁将获胜。
对于树上的节点 \(u\) 计算其子树游戏的 \(\text{SG}\) 函数,于是可以得到 \(\text{SG}(u)\) 为其所有子节点的 \(SG\) 函数值加 \(1\)(加一条边),叶子节点的 \(\text{SG}\) 函数值为 \(0\),于是转化为普通 Nim 游戏求解。
反 Nim(Anti-Nim)游戏
同普通取石子游戏规则,但是不能操作的人胜利。
设 \(T=\oplus_{i=1}^{n}a_{i}\)。
结论:
- 当全部 \(a_i=1\),如果有奇数堆石子则先手必败,有偶数堆则先手必胜。
- 当至少一个 \(a_{i}>1\),\(T\neq 0\) 时则先手必胜,否则先手必败。
证明可见 oi-wiki
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 55;
int T, n;
signed main(){
cin.tie(0)->sync_with_stdio(0);
cin >> T;
while (T--){
cin >> n;
int flag = 0, flag2 = 0, x, ans = 0;
for (int i = 1; i <= n; ++i){
cin >> x;
if (x > 1)flag = 1;
if (x != 1)flag2 = 1;
ans ^= x;
}
if (!flag2 && (n & 1))cout << "Brother\n";
else if (!flag2 && !(n & 1))cout << "John\n";
else {
if (flag && ans)cout << "John\n";
else cout << "Brother\n";
}
}
return 0;
}

浙公网安备 33010602011771号