博弈论笔记
博弈论
公平组合游戏
公平组合游戏(Impartial Game)的定义如下:
\(\bullet\) 游戏有两个人参与,二者轮流做出决策,双方均知道游戏的完整信息;
\(\bullet\) 任意一个游戏者在某一确定状态可以作出的决策集合只与当前的状态有关,而与游戏者无关;
\(\bullet\) 游戏中的同一个状态不可能多次抵达,游戏以玩家无法行动为结束,且游戏一定会在有限步后以非平局结束。 ——— oi-wiki
倒着 dp
Nim 游戏
经典例题
有 \(n\) 堆球,每次可以在任意一个堆取任意个球,问先手必胜还是后手必胜。
结论:
- 对于状态的异或和为 0 的,必然不可能通过一次操作使异或和为 0,因为对于每个要被取的堆,只有当堆的数量不变才能使异或和为 0。
- 对于状态的异或和不为 0 的,必然有一种方案可以使异或和变为 0,可以通过二进制来构造。
- 当所有堆的大小为 0 时,那么必然是必败局面,此处设为初始状态。
那么对于初始状态,可以用一次操作使其变成异或和不为 0 的,再用一次即可变成异或和为 0 的,所以统计异或和,如果异或和为 0 则先手必败,否则先手必胜。
变种
CSES-1099
对于奇数编号台阶的,如果你动它,那么对手可以用同样的方式动回来,当动到 1 时,就动不了了,所以没有贡献;对于偶数编号台阶,如果你动它,会动到奇数编号台阶,就没有贡献了,相当于就是消掉了,那就变回了经典的 Nim 游戏。
SG 函数
SG 函数定义为:所有的后继节点的 SG 函数的 mex,而所有必败节点的 SG 为 0,那么所有的必胜节点的 SG 的值就不为 0。
然而 SG 函数依旧满足 Nim 游戏的三个结论(这里就不再解释了),所以答案求法是一样的。
SG 函数可以用来求答案也可以用来找规律。
例题
CSES-2207
通过找规律我们猜测(嗯,就是猜测)答案为 0 的可能性特别小,所以只要把所有 SG 为 0 的记下来,然后输出时看看 n 是否被记录过 SG 为 0 即可。
所以总的来说就是特别的玄学……
非常好写的博弈论题:CF1823E,不过前期准备可能有点复杂~,也就浅浅代码 \(15\) 分钟,前期准备半天罢了。
#include <iostream>
#include <vector>
using namespace std;
const int MaxN = 2e5 + 10;
int n, l, r, sum, ans;
vector<int> g[MaxN];
bool vis[MaxN];
void DFS(int x) {
if (vis[x]) {
return;
}
vis[x] = 1, sum++;
for (int i : g[x]) {
DFS(i);
}
}
int main() {
cin >> n >> l >> r;
for (int i = 1, u, v; i <= n; i++) {
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
sum = 0;
DFS(i);
if (l <= sum && sum <= (l + r - 1)) {
ans ^= sum / l;
}
}
}
cout << (ans ? "Alice" : "Bob") << endl;
return 0;
}