从0开始的博弈论
写在前面
菜鸡乱学
被 D 了,不学了
参考资料
Attack佬的博弈总结
扶咕咕的【博弈论】Nim游戏
YYB佬的博弈论总结
巴什博奕
巴什博奕是最简单的一种博弈。
问题模型
两个绝顶聪明的人在玩取石子游戏,一共有 \(n\) 颗石子,游戏规则如下:
- 两个人轮流取石子.
- 每次可以取 \(1\sim m\) 颗石子,不能不取.
- 最先不能取到石子的一方失败.
现在让你判断谁会获胜。
绝顶聪明的意思就是指都会按当前对自己最优的策略取石子,不会出现故意让对手赢的情况。
问题分析
分多种情况考虑:
-
只有 \(1\sim m\) 颗石子。
显然先手必胜,直接取走全部石子即可。
-
有 \(m+1\) 颗石子。
此情况先手必败,假设先手选出 \(x\) 颗石子,那么后手必然会选出剩下的 \(m+1-x\) 颗石子,之后先手无法再选,也就是说无论先手选出多少颗石子,后手都可以把石子取完。
-
有 \(m+1\sim 2\times m\) 颗石子。
此情况先手必胜,先手可以选出若干颗石子,使石子剩下 \(m+1\) 颗,然后后手就遇到了情况2,后手无论取出多少颗,先手都可以把剩下的石子取完。
由上面的分析容易发现当一个人面对 \(m+1\) 颗石子时,他就必败了。
推广至一般的情况:
-
当 \((m+1)|n\),即 \(n\) 是 \(m+1\) 的倍数时,先手必败。
假设有 \(n=k\times(m+1)\) 颗石子。
先手取出 \(x\) 颗石子,后手就可以取出 \(m+1-x\) 颗石子,石子变为 \((k-1)\times(m+1)\),一直这样到最后先手面对 \((m+1)\) 颗石子,由上面的分析可知这个时候他就必败了。
-
否则先手必胜。
设石子有 \(n=k\times(m+1)+r\) 颗,先手取出 \(r\) 颗石子,就能让后手面对情况1,此时先手必胜。
这样就分析完了全部情况,判断 \(n\) 是否是 \(m+1\) 的倍数即可。
代码实现
#include <bits/stdc++.h>
using namespace std;
int n, m;
int main() {
cin >> n >> m;
if (n % (m + 1) == 0) puts("houshou");
else puts("xianshou");
return 0;
}
例题
裸题,直接判断即可。
HDU 1847 Good Luck in CET-4 Everybody!
类似巴什博奕,但是又不太像,仔细思考吧。
裸题,转化一下即可。
裸题,转化一下即可。
有点耗脑子了,但是也不难。
Nim游戏
问题模型
两个绝顶聪明的人在玩取石子游戏,一共有 \(n\) 堆石子,规则如下:
- 两个人轮流取石子.
- 可以取走一整堆石子,也可以取走一堆石子里的任意个,但是不能跨堆取,不能不取.
- 最先不能取到石子的一方失败.
判断谁能获胜。
问题分析
解决方法很简单,直接求出每一堆石子数量的异或和,如果异或和为 \(0\) 则先手必败,否则先手必胜。
nim游戏的结束条件很简单,当所有石子的数量都为 \(0\) 时,下一个拿石子的人就失败了,此时的局面是 \(0\bigoplus0\bigoplus0...\bigoplus0=0\)。
设每一堆的石子数为 \(a_i,i\in{[1,n]}\)。
- 对先手来说,如果当前局面是 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=k\)。那么当前石子中必定存才一个 \(a_i\) 满足 \(a_i\) 的二进制表示里存在 \(k\) 的最高位,否则 \(k\) 的最高位异或和应该是 \(0\)。令这个 \(a_i\bigoplus k\),局面就变成了 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=0\),此时先手必胜。
- 对于先手来说,如果当前局面是 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=0\),那么我们不可能将某个 \(a_i\) 异或一个数字之后使得 \(a_1\bigoplus a_2\bigoplus a_3...\bigoplus a_n=0\),此时先手必败。
代码实现
#include <bits/stdc++.h>
using namespace std;
int n, a[A];
inline void solve() {
cin >> n;
int now = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
now = now ^ a[i];
}
if (now) puts("Yes");
else puts("No");
}
int main() {
int T = read();
while (T--) solve();
}
例题
板子题。