(笔记)博弈论 公平组合游戏
博弈论
优化策略取得最大收益的理论方法。
公平组合游戏
玩家可以对局面进行全盘操作,该操作与轮到哪位玩家没有关系。如中国象棋、国际象棋、围棋等只能操纵己方棋子不能算是公平组合游戏。
领悟知识点:哦哦哦哦哦哦。
考题:我们充分发扬人类智慧,观察到二进制……异或……
考场:打表。
博弈图 有向图游戏 SG 函数
将游戏中的状态视为节点,分为必败态和必胜态。
每次操作可以转移到博弈图中的任意后继节点,如果没有后继就称为必败态。如果存在任意一个后继为必败态,那么一定可以通过移到这个后继使对手必败,则该状态为必胜态。反之如果所有后继都为必胜态,该状态就也是必败态。
对于状态 \(x\),我们不妨让 \(\text{SG}(x)\) 表示该状态必胜还是必败。如果为 \(0\) 就是必败,大于 \(0\) 就是必胜。运算 \(\text{mex(S)}\) 表示后继集合 \(S\) 中不存在的最小自然数。显然转移 \(\text{SG}(x)=\text{mex}(S)\) 是一个合法的转移。
为什么不能直接设必胜态为 \(1\),必败态为 \(0\)?这样做也没错,但是会损失大量信息。在之后的性质定理中,我们推出了一个游戏局面只有走向 \(\text{SG}(x')<\text{SG}(x)\) 的后继 \(x'\) 才是有意义的,因为 \(\text{mex}\) 有一个很好的性质,\(\text{mex}=x(x>0)\) 说明后继中存在 \(0,1,...,x-1\)。走向 \(\text{SG}(x')>\text{SG}(x)\) 的后继就意味着 \(x'\) 的后继中一定存在 \(\text{SG}(i)=\text{SG}(x)\) 的 \(i\),那么移到该节点就可以抵消上一次行动。
Nim 游戏
两位玩家,轮流行动,\(n\) 堆石子,每堆 \(a_i\) 个,每次行动选择任意一堆取任意石子,取完胜利。
这里延伸出了比较好的结论,按位异或表示为 \(\oplus\),那么当且仅当 \(\oplus_{i=1}^n a_i\neq 0\) 时处在必胜态。为什么?这是一个比较反直觉的结论,本质上其实是我们利用了题目的性质(只能使 \(a_i\) 变小),以及异或运算本身的特殊性(异或是异或的逆运算),因此我们用它来构造必胜态和必败态。
首先对于 \(\forall i,a_i=0\),该状态肯定为必败态,符合结论。
对于必胜态 \(\exist i,a_i\neq 0\),我们需要证明它的后继中至少有一个必败态,不妨令 \(\oplus_{i=1}^n a_i=k\neq 0\),需要更改的是 \(a_i\),那么更改后的 \(a_i'=a_i\oplus k\),我们需要清楚这个东西实际上是把 \(a_i\) 的一个为 \(1\) 的位变成了 \(0\),然后该位以下的位置重新打乱,定有 \(a_i'<a_i\),这是符合游戏规则的。接下来解释为什么是这样的,不妨令 \(k\) 有最高为 \(1\) 的位置 \(d\) 使得 \(2^d\le k < 2^{d+1}\),那么一定存在奇数个 \(a_j\) 使得 \(a_j\) 第 \(d\) 位是 \(1\),从中任选一个发现其结构是比 \(d\) 更高的位与 \(k\) 异或后不变。
Proof:考虑反证法,如果改变说明:
- 存在更高位使得 \(a_j\) 在该位上为 \(0\),\(k\) 在该位上为 \(1\),与 \(k\) 的最高位为 \(d\) 矛盾。
- 存在更高位使得 \(a_j\) 在该位上为 \(1\),\(k\) 在该位上为 \(1\),与 \(k\) 的最高位为 \(d\) 矛盾。
所以任选一个 \(a_j\) 即可。
接下来证明 \(\oplus_{i=1}^n a_i = 0\) 无法达到必败态。显然要达到必败态也需要 \(\oplus_{i=1}^n a_i' = 0\),而根据异或运算改变的那个 \(a_i'=a_i\oplus 0=a_i\),没有改变,是不符合游戏规则的。
SG 定理
在 \(n\) 个有向图的组合游戏中,存在起点 \(s_1,...s_n\),当且仅当 \(\oplus_{i=1}^n\{\text{SG}(s_i)\}\neq 0\) 时处在必胜态。
Proof:考虑数学归纳法,由当前状态 \(x\) 及其集合 \(s_1,...,s_n\) 转移到后继状态 \(x'\) 及其集合 \(s_1',...,s_n'\),如果 \(x\) 合法能推出合法的 \(x'\) 那么定理成立。显然对于所有 \(\text{SG}(s_i)=0\) 的 \(x\) 为必败态,这是成立的。
这时每步操作可以选取一个 \(s_i\),令其变大或变小(转移到更大或更小的 SG 值,对于 \(s_i>0\),仅在后继中不可能存在 \(i\),其他数都有可能存在,所以该转移合法)。
如果变大,那么由于 \(\text{mex}(S)=x(x>0)\) 说明后继中存在 \(0,1,...,x-1\)。走向 \(\text{SG}(x')>\text{SG}(x)\) 的后继就意味着 \(x'\) 的后继中一定存在 \(\text{SG}(i)=\text{SG}(x)\) 的 \(i\),那么移到该节点 \(i\) 就可以抵消上一次行动,所以变大是无效的。
如果变小,这就变成了一个 Nim 游戏,参照前面的推论可以得到 SG 定理。
需要注意的是,SG 定理适用于一个游戏划分为若干个子游戏,且所有游戏都操作完才算游戏结束的局面。
Nim 游戏转化为有向图游戏
前面的 SG 定理是有向图游戏转化为 Nim 游戏,那么能否逆推?
我们把 Nim 游戏看成有向图游戏,每堆石子都可以转移到比自己小的状态,那么对于 \(x=0,\text{SG}(x)=0\)。对于 \(x>0\),\(\text{SG}(x)=\text{mex}_{i=0}^{x-1}i=x\),那么利用 SG 定理就可以得到 Nim 和的结论 \(\oplus_{i=1}^n\text{SG}(a_i)\neq 0\) 时处于必胜态,即 \(\oplus_{i=1}^na_i\neq 0\),反推得到了 Nim 和结论。
例题
P10501 Cutting Game
每次游戏都是一次状态分裂,看成分裂成 \(2\) 个有向图游戏,将长宽分别为 \(a,b\) 的网格视为状态 \((a,b)\),有转移如下:\(\text{SG}((a,b))=\text{mex}_{i,j}\{\text{SG}(a,j)\oplus \text{SG}(a,b-j)\cup \text{SG}(i,b)\oplus \text{SG}(a-i,b)\}(j\in[2,b-2],i\in[2,a-2])\)。然后直接 DP 或者记忆化搜索即可,时间复杂度 \(O(n^3)\)。
本题实际上不符合经典公平组合游戏的模型,任意一个子游戏达到 \(1\times 1\) 就会结束游戏,但是我们很清楚其实如果达到了类似 \(2\times 2,3\times 3,2\times 3,3\times 2\),那么这些状态一定都是必败态,我们会选择去剪其它的网格,直到所有都剪完,这就又变成了经典模型。实现中由于保证了 \(2\le W,H\),不需要考虑 \(\text{SG}((x,y))\) 中 \(x=1\lor y=1\) 的情况,因为无法到达,所以特判 \(\text{SG}((1,1))\) 无需写入程序也可以达到相同的效果。这样我们的最终边界就自动变成了上述四种情况。
原理解释就是:只有所有子游戏都会变成 \(2\times 2,2\times 3,3\times 2,3\times 3\) 四个必败局面之一时先手才必败。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=205;
int SG[N][N],n,m;
int sg(int x,int y){
if(SG[x][y]>=0)return SG[x][y];
map<int,bool>mp;
for(int i=2;i<=y-2;i++)
mp[sg(x,i)^sg(x,y-i)]=1;
for(int i=2;i<=x-2;i++)
mp[sg(i,y)^sg(x-i,y)]=1;
SG[x][y]=0;
while(mp[SG[x][y]])SG[x][y]++;
return SG[x][y];
}
int main(){
for(int i=1;i<=200;i++)
for(int j=1;j<=200;j++)
SG[i][j]=-1;
while(cin>>n){
cin>>m;
cout<<(sg(n,m)?"WIN\n":"LOSE\n");
}
return 0;
}
P2148 [SDOI2009] E&D
先打表找规律。注意不能使用 DP 因为可能出现类似 \((1,5)\leftarrow (2,3)\) 的转移。
用二进制表示集合 \(S\),找到对于所有 \(a+b=c\) 的 \(c\),其 \(\text{SG}\) 值的集合为 \(c-1\) 的二进制表示。\(\text{SG}((a,b))\) 就是 \(a-1\) 按位或 \(b-1\) 的二进制表示下的最小为 \(0\) 的位置,具体证明移步题解区。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+5;
int n,s[N];
int mex(int x){
int i;
for(i=0;x;i++,x>>=1)
if(!(x&1))return i;
return i;
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&s[i]);
int ans=0;
for(int i=1;i<=n;i+=2){
ans^=mex((s[i]-1)|(s[i+1]-1));
}
if(ans)printf("YES\n");
else printf("NO\n");
}
return 0;
}
P2594 [ZJOI2009] 染色游戏
Lemma1:一维和二维翻硬币游戏中,当前局面的 \(\text{SG}\) 值为所有硬币单独存在时的 \(\text{SG}\) 值异或和。
Proof:容易证明构造方案的充分性,定义 \(\text{SG}((x,y))\) 表示翻硬币 \((x,y)\) 且保证子矩形 \((1,1)\) 到 \((x,y)\) 内除 \((x,y)\) 之外硬币正反面不变的 \(\text{SG}\) 值(如果已完成值为 \(0\))。那么通过把 \(cnt\) 个需要翻的硬币分离成 \(cnt\) 个有向图游戏即可,最终 \(\text{SG}\) 就是其异或和。
必要性不会证。很多翻硬币游戏的题都用到了这个结论但是没什么具体翔实的证明,建议网络搜索。
Theorem1:
Proof:考虑 SG 定理类似数学归纳法证明即可,请移步题解区。
最终使用 \(f_i\) 表示 \(\text{SG}\) 值第 \(i\) 位上是否为 \(1\),然后直接计算即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=105;
char s[N];
int SG[N][N];
bool f[N*2];
int main(){
for(int i=1;i<=100;i++)
SG[i][1]=SG[1][i]=log2(i&(-i));
for(int i=2;i<=100;i++)
for(int j=2;j<=100;j++)
SG[i][j]=i+j-2;
int T;scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<=200;i++)f[i]=0;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]=='T')f[SG[i][j]]^=1;
}
bool tf=0;
for(int i=0;i<=200;i++)
if(f[i])tf=1;
if(tf)printf("-_-\n");
else printf("=_=\n");
}
return 0;
}

浙公网安备 33010602011771号