【重建计划】博弈论学习笔记
博弈论这个东西非常的抽象,主要研究具有竞争或对抗性质的对象,在一定规则下产生的各种行为。博弈论考虑游戏中的个体的预测行为和实际行为,并研究它们的优化策略。
一般来说,博弈论分为如下三个大部分:
- 公平组合游戏
公平组合游戏指的是:该游戏有两个人参与,其中这两个人均知道游戏的完整信息,任意一个游戏者在某一确定状态可以作出的决策集合只与当前的状态有关,而与游戏者无关,同时保证每一个状态是只存在一次的,以玩家无法继续移动为结束,且游戏一定在有限步数下完成(指没有平局)。
- 非公平组合游戏
非公平组合游戏指的是:和公平组合游戏相同,只是游戏者在某一确定状态可以做出的决策集合与游戏者有关。
- 反常游戏
按照传统的游戏规则进行游戏,但是其胜者为第一个无法行动的玩家。
什么事“游戏者在某一确定状态可以做出的决策集合与游戏者的关系”?就是说游戏者 \(A\) 和 \(B\) 的可操作集合不同,比如说取石子,两个取的是同一些石子,这就是和与游戏者无关,而如果两个人取得物品有限制,比如说围棋中黑手不能够动白子,这样就是与游戏者有关。
一.公平组合游戏 ICG
1.1 博弈图和操作状态
任何一个公平组合游戏都可以转化为有向图游戏。具体方法是,把每个局面(游戏过程中面临的状态)看成图中的一个节点,并且从每个局面向沿着合法行动能够到达的下一个局面连有向边。
组合游戏向图的转化,实际上就是实现了建立了博弈论的数学模型。此时对于这个图,由于 \(\text{ICG}\) 和操作者无关而仅于当前状态有关,所以这个图就不在乎是谁操作到当前这个节点的状态,而仅仅保存这个节点的状态可以到达的其它状态。
接下来是在 \(\text{ICG}\) 中的两个核心的状态:
- 必胜态:存在一种策略,可以让剩余的状态变成 将必败态留给对手 的局面。即先手必胜的状态。
- 必败态:不管采取什么策略,这一步行动后对手都处于必胜态的局面。即后手必胜的状态(先手必败)。
博弈论三大定理:
- 没有后继状态的是必败态。
- 可以移动到必败态的是必胜态。也可以表述为:一个状态是必胜状态当且仅当存在至少一个必败状态为它的后继状态。
- 只能移动到必胜态的是必败态。也可以表述为:一个状态是必败状态当且仅当它的所有后继状态均为必胜状态。
博弈论有向图的核:
给定一张 DAG 图 \(G=(V,E)\),如果 \(V\) 的一个子集 \(S\) 满足:
- \(S\) 是独立集。(\(S\) 中的节点两两不相邻)
- \(S\) 在 \(V\) 中的补集中的点都可以一步到达集合 \(S\) 中的点。
则称 \(S\) 是图 \(G\) 的一个核。
由于 \(S\) 中的所有的状态都没有出度,根据博弈论第一定理,核内节点对应的状态都为必败态。
如果博弈图是一个 DAG ,我们通过这三个定理+核的判定就能用 \(O(N+M)\) 的时间(\(N\) 为状态总数,\(M\) 为边数)得出每个状态是必胜态还是必败态。
1.2 Nim 博弈问题
整局游戏第一个行动的称为先手,第二个行动的称为后手。若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。最优策略是指,若在某一局面下存在某种行动,使得行动后对手面临必败局面,则优先采取该行动。同时,这样的局面被称为必胜。
Nim 博弈不存在平局,只有先手必胜和先手必败两种情况。
通过画 DAG 可以求出但是复杂度过高,接下来我们说明 Nim 的必胜必败定理:
- 若 \(a_1\oplus a_2\oplus a_3\dots \oplus a_n\ne 0\) 则先手必胜,否则先手必败。
接下来根据博弈论三大定理进行证明:
显然的,一个数的二进制可以表明这个数的大小。首先是第一定理显然成立。接下来证明定理二:必胜态的后继中至少存在一个必败态。设 \(a_1\oplus a_2\oplus a_3\dots \oplus a_n=s\ne 0\),设 \(s\) 的二进制为 \(1\) 的最高位为第 \(k\) 位,那么根据异或的性质,在 \(a_1\) 到 \(a_n\) 中,一定有奇数个 \(a_i\) 的第 \(k\) 位为 \(1\)。用其中的任意一个 \(a_i\) 做操作: \(a_i\oplus s\) 替换掉 \(a_i\) (相当于取石子)则会使得 \(a_i\) 的第 \(k\) 位变成 \(0\) 从而使得 \(a_1\oplus a_2\oplus a_3\dots \oplus a_n= 0\)。而 \(0\) 是必败态(根据前面的 Nim 的必胜必败定理)。
接下来证明定理三:必败态的后继中都是必胜态。此时 \(a_1\oplus a_2\oplus a_3\dots \oplus a_n= 0\) 那么同位置上的 \(1\) 的个数都是偶数,那么此时无论我们把哪一个位置的 \(1\) 的个数进行操作,都有一个可行的最优策略使得接下来 \(a_1\oplus a_2\oplus a_3\dots \oplus a_n\ne 0\)。证毕。
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
using namespace std;
int t,n,a[10005];
int main(){
cin>>t;
while(t--){
cin>>n;
int sum=0;
for (int i=1;i<=n;i++){
cin>>a[i];
sum^=a[i];
}
if(sum!=0) cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
1.2 Bash 博弈问题
又称巴什博弈问题。前面的 Nim 问题取石子的个数任意,而 Bash 问题取石子的个数有限制。前面的这个例题是拓展的 Bash 博弈问题,需要 SG 函数,所以看看前面的原始问题即可。
先给出 Bash 博弈问题的必胜必败定理:
- 若 \(n\equiv0\pmod{m+1}\),则先手必败,否则先手必胜。
这是很显然的,因为 \(1\) 到 \(m\) 的状态一定可以转化到 \(0\) 上去,那么这些状态都是必胜态,因为后面存在一个策略使得下一个状态为必败态(即为 \(0\))。而 \(m+1\) 的状态无论怎么取,后面的状态一定是 \(1\) 到 \(m\) 的必胜态,此时 \(m+1\) 的状态就是必败态,如此反复。可以发现,凡是 \(n\equiv0\pmod {m+1}\) 则必定是先手必败。
1.3 Wythoff 博弈问题
P2252 [SHOI2002] 取石子游戏|【模板】威佐夫博弈
这个博弈问题就没有这么简单了。我们先引入一个定理,我们称其为 \(\text{Betty}\) 定理,这个定理是这么描述的:
对于两个无理数 \(x,y\),如果其满足 \(\frac{1}{x}+\frac{1}{y}=1\)。令两个集合 \(P=\{p\mid p=\left\lfloor{nx}\right\rfloor,n\in N^{+}\}\),\(Q=\{q\mid q=\left\lfloor{my}\right\rfloor,m\in N^{+}\}\)。则必然存在 \(P\cap Q=\varnothing\),\(P\cup Q=N^{+}\)。
该定理如何证明?该定理等价于对于 \(\forall k\in N^{+}\),要么 \(k\in P\) 要么 \(k\in Q\) 且两者不能够同时成立。考虑反证。
如果 \(\exists k\in N^{+}\) 使得 \(k\in P\) 且 \(k\in Q\)。即 \(P\cap Q\ne\varnothing\)。由于 \(x,y\) 是有理数,所以 \(nx,my\) 均不为正整数,所以我们不妨设 \(k<nx<k+1,k<my<k+1\)。则 \(\frac{n}{k+1}<\frac{1}{x}<\frac{n}{k},\frac{m}{k+1}<\frac{1}{y}<\frac{m}{k}\)。两式相加立即得到 \(\frac{n+m}{k+1}<\frac{1}{x}+\frac{1}{y}<\frac{n+m}{k}\)。又 \(\frac{1}{x}+\frac{1}{y}=1\) 所以 \(k<n+m<k+1\)。这说明 \(n+m\) 不是正整数,而前提条件中 \(n,m,k\) 均为正整数,那么 \(n+m\) 也比为正整数,相矛盾,故假设不成立。所以 \(\nexists k\in N^{+}\) 使得 \(k\in P\) 且 \(k\in Q\)。
同理,如果 \(\exists k\in N^{+}\) 使得 \(k\notin P\) 且 \(k\notin Q\)。由于 \(x,y\) 是有理数,所以 \(nx,my\) 均不为正整数,所以我们不妨设 \(nx<k<k+1<(n+1)x,my<k<k+1<(m+1)y\)。则 \(\frac{n}{k}<\frac{1}{x}<\frac{n+1}{k+1},\frac{m}{k}<\frac{1}{y}<\frac{m+1}{k+1}\)。两式相加立即得到 \(\frac{n+m}{k}<\frac{1}{x}+\frac{1}{y}<\frac{n+m+2}{k+1}\)。又 \(\frac{1}{x}+\frac{1}{y}=1\) 所以 \(n+m<k<k+1<n+m+2\)。显然 \(k\) 和 \(k+1\) 应当都是正整数,而显然在 \(n+m\) 到 \(n+m+2\) 中只有一个正整数,于是相矛盾,故假设不成立。所以 \(\nexists k\in N^{+}\) 使得 \(k\notin P\) 且 \(k\notin Q\)。
这样,对于 \(\forall k\in N^{+}\) 不可能同时存在于 \(P,Q\) 中,也不能同时不存在于 \(P,Q\) 中,那么显然存在其中的一个中。
回到 Wythoff 博弈问题上去,以下用整数对 \((a,b)\) 表示两个石子堆的状态,由于 \((a,b)\) 和 \((b,a)\) 本质上一致,所以只考虑 \(a\leq b\) 的情况就行了。先给出 Wythoff 博弈的一般必胜必败状态:
- 第 \(i\) 大的先手必败状态是 \((a_i,b_i)\)。\(a_i=\text{mex}(a_1,b_1,a_2,b_2,\dots,a_{i-1},b_{i-1}),b_i=a_i+i\)
其中,\(\text{mex}(S)\) 指的是集合 \(S\) 中最小未出现整数。接下来进行证明。
现在证明一个必败态后继状态都是必胜状态。因为 \(a_i\) 在前面没有出现过,那么 \(a_i>a_{i-1}\)。所以 \(b_i=a_i+i>a_{i-1}+i-1=b_{i-1}\)。所以为了使这个情况转化为另一个必败态,必然要从两堆各取出同样多的石子。所以 \(a,b\) 的差不变,而其余必败态的 \(a_j,b_j\) 的差 \(j\) 均与 \(a_i,b_i\) 的差 \(i\) 不相同,所以它的所有后继状态均不是必败态,即都是必胜态。
现在证明一个必胜状态必有一个后继状态是必败状态。
-
当 \(a>a_k,b=b_k\) 时,那么只需要从 \(a\) 堆中取出 \(a-a_k\) 个即可。此时 \(a\) 变成 \(a_k\) 而 \(b\) 不变,构成 \((a_k,b_k)\) 必败态。对于 \(a=a_k,b>b_k\) 同理。
-
当 \(a>a_k,b>b_k\) 时,设 \(l\) 为常数且 \(l>0\)。
- 当 \(a<a_{l+k}\) 时,由于在下标 \(k\) 到 \(k+l\) 中的 \(a\) 显然已经不存在没有被取的情况了,所以这个 \(a\) 一定是根据 \(b_1,b_2,b_3,\dots,b_{k+l-1}\) 确定的,所以就是其中之一,于是设 \(a=b_m\) 则显然 \(b>b_m>a_m\) 必然成立,然后就变成了上面的情况。
- 考虑 \(a=a_{l+k}\) 的情况,如果此时 \(b>b_{k+l}\) 那么就退化成了最开始的情况。如果 \(b\ne b_{k+l}\),所以 \(b<b_{k+l}\)。
- 若 \(b=b_m\) 则满足 \(a=a_{l+k}>a_m\)。此时仍然退化为了最开始的情况。
- 如果 \(b=a_m\) 则由于 \(b=a_m>a=a_{l+k}\) 所以 \(m>l+k\),所以 \(0<a_m-a_{k+l}<k+l\),所以将 \(b\) 堆取到 \(b_{a_m-a_{k+l}}\) 个时,\(a\) 堆将取到 \(a_{a_m-a_{l+k}}\) 个,显然为必败态。
综上,必胜状态必有一个后继状态是必败状态。
结合 \(\text{Betty}\) 定理,那么这就相当于在正整数集合 \(N^{+}\) 中取最小的未出现的数,那么这个数显然在集合 \(P\) 或者 \(Q\) 中,所以我们根据它,就可以得到 Wythoff 博弈问题的通项公式:
- 先手必败状态是 \((a,b)\)。\(a=\left\lfloor{n\times \frac{\sqrt{5}+1}{2}}\right\rfloor,b=\left\lfloor{n\times \frac{3+\sqrt{5}}{2}}\right\rfloor\)
显然 \(\frac{1}{\frac{\sqrt{5}+1}{2}}+\frac{1}{\frac{3+\sqrt{5}}{2}}=1\) 那么应用 \(\text{Beatty}\) 定理发现满足以上所述的 \(\text{mex}\) 的性质。
综上,我们得到结论:
- 若对于初始状态 \((a,b)\) 满足 \(a<b\) 则令 \(k=b-a\)。记 \(w=\left\lfloor{k\times \frac{\sqrt{5}+1}{2}}\right\rfloor\),若 \(w=a\) 则先手必败,否则先手必胜。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
int a,b;
int main(){
cin>>a>>b;
if(a>b) swap(a,b);
int k=b-a;
double w=k*((sqrt(5)+1)/2);
if((int)w==a) cout<<"0\n";
else cout<<"1\n";
return 0;
}

浙公网安备 33010602011771号