0.目录
本篇文章梳理如下
- 一些概念
- 经典模型——巴什博弈,威佐夫博弈
- 博弈点搜索
- 组合博弈
- N i m \tt Nim Nim 游戏以及其拓展
- 删边博弈
- 博弈点搜索+( a l p h a alpha alpha - b e t a beta beta 剪枝)
- 博弈论总结
1. 概念
博弈论
概述:多个人在一定约束条件下,利用已掌握的信息
[
1
]
^{[1]}
[1],使自身收益最大化的过程。全是抄的
公平
概述:每个人的操作是不是对等的。比如象棋就是不公平的,因为不能移动别人的棋子
信息对等
概述:每个人所掌握的信息是不是对等的。比如斗地主就是信息不对等的,因为你不知道别人手上的牌
[ 1 ] : [1]\tt: [1]: 这是很重要的,因为有的题目你要站在当事人的角度分析,不能用旁观者的眼光对待。
2. 巴什博弈 & 威佐夫博弈
巴什博弈
题目:有 n n n 个石子,两个人轮流取,每次最多取 m m m 个,最少取 1 1 1 个,取完者获胜,问先手有没有必胜策略。
分析:作为大多数人都会的小奥题,它有一个很显然的解法:每次对方取 k k k 个,就取 m + 1 − k m+1-k m+1−k 个,第一次取 n m o d ( m + 1 ) n\bmod(m+1) nmod(m+1) 个
显然:
{
n
m
o
d
(
m
+
1
)
=
0
先手必败
n
m
o
d
(
m
+
1
)
≠
0
先手必胜
\begin{cases}n\bmod(m+1)=0&\text{先手必败}\\n\bmod(m+1)\ne0&\text{先手必胜}\end{cases}
{nmod(m+1)=0nmod(m+1)=0先手必败先手必胜
代码:
#include<stdio.h>
int main(){
int n,m;
scanf("%d%d",&n,&m);
if(n % (m + 1)) printf("1");
else printf("0");
return 0;
}
威佐夫博弈
题目:有两堆石子,两个人轮流去,每次可以从一堆中取若干个或从两堆中取相同的若干个,取完者获胜,问先手有没有必胜策略。
分析:
Step 1.枚举
由于先手有必胜策略的情况多于没有的情况,于是我们来枚举少数的情况。(为了方便,默认第一堆数量小于等于第二堆)
先手没有必胜策略的情况有: ( 0 , 0 ) , ( 1 , 2 ) , ( 3 , 5 ) , ( 4 , 7 ) , ( 6 , 10 ) ⋯ (0,0),(1,2),(3,5),(4,7),(6,10)\cdots (0,0),(1,2),(3,5),(4,7),(6,10)⋯
规律:记第
i
i
i 项为
(
a
i
,
b
i
)
(a_i,b_i)
(ai,bi),可得
a
0
=
b
0
=
0
,
a
i
=
mex
{
a
0
,
b
0
,
…
,
a
i
−
1
,
b
i
−
1
}
,
b
i
=
a
i
+
i
,
mex
a_0=b_0=0,a_i=\operatorname{mex}\{a_0,b_0,\dots,a_{i-1},b_{i-1}\},b_i=a_i+i,\operatorname{mex}
a0=b0=0,ai=mex{a0,b0,…,ai−1,bi−1},bi=ai+i,mex 表示一个集合中最小没有出现过的自然数
于是任何自然数都一定包含在先手没有必胜策略的局面中(根据上文
mex
\operatorname{mex}
mex 规律可得)
通项公式下文再讲
Step 2.解法
如果给定一个局势 ( x , y ) (x,y) (x,y),如何判断是不是必胜状态呢?
用上面的式子应算显然不可能,我们要考虑通项公式
通项公式:记 ϕ = \phi= ϕ= 黄金比的倒数 = 1 + 5 2 =\dfrac{1+\sqrt5}{2} =21+5,则 a i = ⌊ i ϕ ⌋ , b i = ⌊ i ϕ 2 ⌋ a_i=\left\lfloor i\phi\right\rfloor,b_i=\left\lfloor i\phi^2\right\rfloor ai=⌊iϕ⌋,bi=⌊iϕ2⌋
于是代码如下:(威佐夫博弈模板)
#include<stdio.h>
#include<math.h>
int main(){
int n,m,t;
scanf("%d%d",&n,&m);
if(n > m) t = n,n = m,m = t; // 保证n<m
double phi = (1.000 + sqrt(5.000)) / 2.000; // 计算phi
if(florr(phi * (m - n)) == n) printf("0"); // n=a[m-n]=floor(phi*(m-n))
else printf("1");
return 0;
}
3.博弈点搜索
对于前两个经典博弈例子,都有神奇的 O ( 1 ) O(1) O(1) 公式,但有没有涵盖大部分博弈的解法呢,答案是:有!!
而且,还是每天都给我们帮助,让我们受益匪浅的——DFS!!
惊不惊喜意不意外 不过标题似乎就说明了
我们把 DFS 递归走到的状态理解为一棵 DFS 树,树上的每一个节点都是一种状态,把它称之为 \, 博弈树(其实是一个 D A G \rm{DAG} DAG(有向无环图),但是我们把由不同方法到达的同一种状态看为不同节点,就会得到一棵树)
DFS 过程:把状态放到 DFS 函数的参数里,并且为每一种状态打上标记,还可以用记忆化搜索优化已经搜索过的节点。
那标记呢?标记分为 P P P 和 N N N
- P : Previous Position P:\text{Previous Position} P:Previous Position 代表上一步的人赢,就是必败节点
- N : Now Position N:\text{Now Position} N:Now Position 代表这一步的人赢,就是必胜节点
那我们怎么标记呢? 4 \tt4 4 条规则 ↓ \downarrow ↓
- 终止状态是 必败点
- 一步只能到达 必胜点 的都是 必败点
- 一步只能到达 必败点 的都是 必胜点
- 一步可以到达 必胜点 和 必败点 的也是 必胜点
或者概括一下 ↓ \downarrow ↓
- 终止状态标记为 必败点
- 一步只能到达 必胜点 的都是 必败点
- 一步可以到达 必败点 的都是 必胜点
为什么呢?(这里我们以 威佐夫博弈 为例,其它自己推导不难)
证明 1: \texttt{1:} 1: 威佐夫终止状态为 ( 0 , 0 ) (0,0) (0,0),是先手必败点。
证明 2: \texttt{2:} 2: 定义这一堆为 ( a , b ) (a,b) (a,b)
只取一堆。因为任何一个非 0 0 0 自然数只会在 a i , b i a_i,b_i ai,bi 中出现一次。如果 a = 0 a=0 a=0,则 b = 0 b=0 b=0,也是必败点。
两堆都取。如果两堆都取同样数量的石子,它们的差不变。而 b i − a i = i b_i-a_i=i bi−ai=i,不存在 i ≠ j i\ne j i=j,使得 b i − a i = b j − a j b_i-a_i=b_j-a_j bi−ai=bj−aj。
证明 3: \texttt{3:} 3: 定义这一堆为 ( a , b ) (a,b) (a,b)
- 如果 a = b a=b a=b,令 a ← a − a , b ← b − a a\gets a-a,b\gets b-a a←a−a,b←b−a,变成必败点 ( 0 , 0 ) (0,0) (0,0)
- 如果 a = a i , b = b i a=a_i,b=b_i a=ai,b=bi,不满足 ( a , b ) (a,b) (a,b) 是必胜点
- 如果 a = a i , b > b i a=a_i,b\gt b_i a=ai,b>bi,令 b ← b i b\gets b_i b←bi,变成必败点 ( a i , b i ) (a_i,b_i) (ai,bi)
- 如果 a = a i , b < b i a=a_i,b\lt b_i a=ai,b<bi,令 a ← a b − a , b ← a b − a + b − a a\gets a_{b-a},b\gets a_{b-a}+b-a a←ab−a,b←ab−a+b−a,变成必败点 ( a b − a , b b − a ) (a_{b-a},b_{b-a}) (ab−a,bb−a)
- 如果 a ≠ a i a\ne a_i a=ai,则一定 a = b i a=b_i a=bi,令 b ← a i b\gets a_i b←ai,变成必败点 ( a i , b i ) [ 2 ] (a_i,b_i)^{[2]} (ai,bi)[2]
[ 2 ] : [2]: [2]: 根据 mex \operatorname{mex} mex 的定义,所有自然数都在 a i , b i a_i,b_i ai,bi 中出现过。
4.组合博弈
考虑一枚(或多枚)棋子,在一个 D A G \mathrm{DAG} DAG(有向无环图)中,两个人轮流将其进行移动 1 1 1 步,无法移动者输。
棋子可以理解为状态,有向无环图就是由不同的状态构成的有向无环图,移动就代表进入后继状态。
这里的博弈游戏中,定义 SG \operatorname{SG} SG 函数 SG ( x ) = mex { SG ( y ) ∣ y \operatorname{SG}(x)=\operatorname{mex}\{\operatorname{SG}(y)\mid y SG(x)=mex{SG(y)∣y 是 x x x 的后继状态集 } \} } (它的作用后面再说)
mex \operatorname{mex} mex 还是一个集合中最小没出现过的自然数
SG \operatorname{SG} SG 函数性质如下
- SG ( \operatorname{SG}( SG(终止节点 ) = 0 )=0 )=0(因为它的后继状态集是空集)
-
SG
(
\operatorname{SG}(
SG(非终止节点
)
)
) 分两种情况来考虑
- SG ( x ) = 0 \operatorname{SG}(x)=0 SG(x)=0,则 SG ( y ) ≠ 0 , y \operatorname{SG}(y)\ne0,y SG(y)=0,y 是 x x x 的后继状态集
- SG ( x ) ≠ 0 \operatorname{SG}(x)\ne0 SG(x)=0,则存在 SG ( y ) = 0 , y \operatorname{SG}(y)=0,y SG(y)=0,y 是 x x x 的后继状态集
是不是和之前的必胜点和必败点有点像?其实对于一枚棋子移动的问题, SG = 0 \operatorname{SG}=0 SG=0 就可以直接判断是必败点,反之为必胜点。
那如果是多枚棋子的移动呢?如果有 k k k 枚棋子,此时就会有 k k k 个局面 G 1 , G 2 , … , G k G_1,G_2,\dots,G_k G1,G2,…,Gk,那么它们的和 G G G 满足 SG ( G ) = SG ( G 1 ) xor SG ( G 2 ) ⋯ xor SG ( G k ) \operatorname{SG}(G)=\operatorname{SG}(G_1)\operatorname{xor}\operatorname{SG}(G_2)\cdots\operatorname{xor}\operatorname{SG}(G_k) SG(G)=SG(G1)xorSG(G2)⋯xorSG(Gk)
扯了这么多, SG \operatorname{SG} SG 函数,如何计算?
答案就是 \, DFS(和上面那个不是一个东西,上面就是纯粹 DFS, SG \operatorname{SG} SG 函数说白了还是一些规律)。
代码:好的相信你已经会了((
#include<stdio.h>
#include<string.h>
#include<vector>
const int maxn = 2001;
int SG[maxn],vis[maxn],n,m,k,ans = 0;
// SG就是当前的SG函数值
// vis[i]表示当前局面有没有SG值为i的节点,为了重复使用,让它记录上一次算出局面SG值为i的节点
std::vector<int> G[maxn]; // 图
void dfs(int x){
if(SG[x] != -1) return;// 已经算过就不算了
for(int i = 0;i < G[x].size();++i) dfs(G[x][i]);// 递归算子节点
for(int i = 0;i < G[x].size();++i) vis[SG[G[x][i]]] = x;// 给SG值打上标记
for(int i = 0;i <= n;++i) if(vis[i] != x){SG[x] = i;return;}// 最粗暴的mex计算方法
}
int main(){
memset(SG,-1,sizeof SG);// 默认没算过
memset(vis,-1,sizeof vis);// 默认为一个不存在的节点,0也行
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i <= m;++i){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);// 注意是有向图
}
for(int i = 1;i <= k;++i){
int x;
scanf("%d",&x),dfs(x);// 每次都要dfs
ans ^= SG[x];// 直接异或就可以了
}
if(ans) printf("win\n");
else printf("lose\n");
return 0;
}
5.Nim游戏
N i m \tt{Nim} Nim 游戏规则如下:
有 n n n 堆石子 a 1 , a 2 , … a n a_1,a_2,\dots a_n a1,a2,…an,两人轮流从任意一组取若干个石子,谁取完谁赢,问先手有没有必胜策略。
解法:
N
i
m
\tt{Nim}
Nim 游戏作为组合博弈的一种,也有显然的 DFS
SG
\operatorname{SG}
SG 函数解法,但是
\,
如果就这我还写这一类干嘛
更简单的方法
:
{
a
1
xor
a
2
⋯
xor
a
n
=
0
先手必败
a
1
xor
a
2
⋯
xor
a
n
≠
0
先手必胜
\begin{cases}a_1\operatorname{xor}a_2\cdots\operatorname{xor}a_n=0&\text{先手必败}\\a_1\operatorname{xor}a_2\cdots\operatorname{xor}a_n\ne0&\text{先手必胜}\end{cases}
{a1xora2⋯xoran=0a1xora2⋯xoran=0先手必败先手必胜
其中 xor \operatorname{xor} xor 表示异或运算 ⊕ \oplus ⊕
证明:
这个解法成立需要满足三个条件:
- 终止状态是必败节点
- 由一个必败节点,无论怎么走都是必胜节点
- 由一个必胜节点,存在一种走法到必败节点
证明 1: \texttt{1:} 1: 因为必败态 0 xor ⋯ 0 = 0 0\operatorname{xor}\cdots0=0 0xor⋯0=0,所以成立
证明
2:
\texttt{2:}
2: 记我们要取的是第
i
i
i 堆,根据 异或 的性质,可得
a
1
xor
a
2
⋯
xor
a
i
−
1
xor
a
i
+
1
⋯
xor
a
n
=
a
i
a_1\operatorname{xor}a_2\cdots\operatorname{xor}a_{i-1}\operatorname{xor}a_{i+1}\cdots\operatorname{xor}a_n=a_i
a1xora2⋯xorai−1xorai+1⋯xoran=ai
而只有
a
i
xor
a
i
=
0
a_i\operatorname{xor}a_i=0
aixorai=0 ,所以无论把
a
i
a_i
ai 改成什么,都得不到必败状态
证明 3: \texttt{3:} 3: 如果 a 1 xor a 2 ⋯ xor a n = x a_1\operatorname{xor}a_2\cdots\operatorname{xor}a_n=x a1xora2⋯xoran=x,此时可以找到一个 a i a_i ai, a i a_i ai 在 x x x 的最高二进制位上是 1 1 1,那么 a i xor x ≥ 0 a_i\operatorname{xor}x\ge0 aixorx≥0,于是令 a i ← a i xor x a_i\gets a_i\operatorname{xor}x ai←aixorx 即可
代码:
#include<stdio.h>
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,res = 0;
scanf("%d",&n);
for(int i = 1;i <= n;++i){
int ai;
scanf("%d",&ai);
res ^= ai;
}
if(res) puts("Yes");
else puts("No");
}
return 0;
}
Anti-Nim 游戏
规则:与 N i m \tt{Nim} Nim 一样,不过是取完者败。
于是我们脑袋里出现一个显然的思路:把上一份代码 Yes
和 No
反过来。不过你以为就会这么简单?
假如有一堆,两个石子,如果用如上思路,先手直接取两个然后自己让自己输掉。但最佳策略应该是先取一个让对方输掉。
结论:
- 石子堆数异或和 = 0 =0 =0 且每堆只有 1 1 1 个石子为必胜状态
- 石子堆数异或和 ≠ 0 \ne0 =0 且至少一堆石子个数大于 1 1 1 为必胜状态
- 其余都是必败状态
证明:
证明 1: \texttt{1:} 1: 只有偶数堆 1 1 1 才能异或得 0 0 0,两人每次取一堆,后手取最后一堆,先手必胜。
证明 2,3: \texttt{2,3:} 2,3:
只有一堆石子个数大于 1 1 1(情况 2 1 \tt2_1 21)
- 有偶数堆 1 1 1,可以把大于 1 1 1 这堆取完,那么就会剩下奇数堆 1 1 1,再加上是后手先走,先手必胜。
- 有偶数堆 1 1 1,如果想异或和为 0 0 0,剩下那堆必须为 0 0 0,不可能
- 有奇数堆 1 1 1,可以把大于 1 1 1 这堆取得剩下 1 1 1 个,那么就会剩下奇数堆 1 1 1,再加上后手先走,先手必胜。
- 有奇数堆 1 1 1,如果想异或和为 0 0 0,剩下那堆必须为 1 1 1,矛盾
于是定理 2 \tt2 2 得到证明。
有多堆石子个数大于 1 1 1(情况 2 2 ,3 \tt{2_2}\texttt{,3} 22,3)
- 异或和为 0 0 0,此时随便怎么走都会转化为异或和不为 0 0 0 的状态。那么一定可以转化为两种情况
- 有大于等于两堆的石子个数 > 1 \gt1 >1,变成下面的情况 3 ↓ \tt3\downarrow 3↓。
- 有一堆石子个数 > 1 \gt1 >1,这就是已证的定理 2 1 \tt{2_1} 21。
- 异或和不为 0 0 0,此时根据 N i m \tt Nim Nim 的证明,必然存在一种变成异或和为 0 0 0 的走法。(这是情况 3 \tt3 3)
代码:
#include<stdio.h>
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,res = 0,cnt = 0;
scanf("%d",&n);
for(int i = 1;i <= n;++i){
int x;
scanf("%d",&x);
res ^= x,cnt += x > 1;
}
if(res == 0 && cnt == 0) puts("John");
else if(res && cnt) puts("John");
else puts("Brother");
}
return 0;
}
S-Nim
这个名字来源于 一道题。
题意: N i m \tt{Nim} Nim 游戏,但是每次可以取的石子数量必须是 s 1 , s 2 , ⋯ , s k s_1,s_2,\cdots,s_k s1,s2,⋯,sk 中的一个。
解法:这题没有 O ( 1 ) O(1) O(1) 解法,于是直接 DFS SG \operatorname{SG} SG 值即可。
注意两点优化:
- 先把 s s s 数组排好序,这样 DFS 的时候,找到 s i > x s_i>x si>x 就直接退出,节省时间
- 每 m m m 组测试样例并不是相互独立的(因为这 m m m 组数据的步数集都是相同的),可以在读入样例的开头先把 SG \operatorname{SG} SG 数组清空为 − 1 -1 −1。(这卡了我好久)
放代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
inline int Read(){
register int x = 0,f = 1,c = getchar();
for(;c < 48 || c > 57;c = getchar()) f = c == 45 ? -1 : 1;
for(;c >= 48 && c <= 57;c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int SG[10001],step[101],vis[10001],n,m,k,ans = 0;
void dfs(int x){
if(SG[x] != -1) return;
for(int i = 1;i <= k && step[i] <= x;++i) dfs(x - step[i]);
for(int i = 1;i <= k && step[i] <= x;++i) vis[SG[x - step[i]]] = x;
// 注意上面的 step[i] <= x,可以节省时间(因为排了序在step[i]不行时直接退出循环)
for(int i = 0;;++i) if(vis[i] != x){SG[x] = i;return;}
}
int main(){
while(k = Read()){
for(int i = 1;i <= k;++i) step[i] = Read();
std::sort(step + 1,step + k + 1);// 排序!!
m = Read();
memset(SG,-1,sizeof SG);// 在m组数据开头清空即可
while(m--){
ans = 0,n = Read();
for(int i = 1;i <= n;++i){
int x = Read();
dfs(x);
ans ^= SG[x];
}
if(ans) putchar('W');
else putchar('L');
}
putchar('\n');
}
return 0;
}
6. 删边博弈
Easy VERSION
什么毒瘤题,还分 Easy Vision
题目:给定一棵有根树,两个人轮流从树上删边,每删一条边,不与根相连的一部分也会被删去,谁不能再删谁输,问先手有没有必胜策略。
结论:叶子结点的 SG \operatorname{SG} SG 函数值为 0 0 0,其它的节点的 SG \operatorname{SG} SG 函数值为其所有子节点的 SG \operatorname{SG} SG 值 + 1 +1 +1 后的异或和
Q.这么奇葩的结论,怎么让我想?
A. 可以先写 DFS,然后再分析找规律。
注意:学会用 DFS(也就是前文的博弈点搜索)是非常重要的解题方法,你甚至还可以自己模拟搜索。
让我们来看看如何这条定理证明
数学归纳法证明如下:
一:只有一个或两个节点时显然成立
这个很显然,只有一个节点,其 SG \operatorname{SG} SG 值就是 0 0 0,是必败节点,对的。
有两个节点, SG ( r t ) = 1 , SG ( l f ) = 0 \operatorname{SG}(rt)=1,\operatorname{SG}(lf)=0 SG(rt)=1,SG(lf)=0,那么 r t rt rt 是必胜节点, l f lf lf 是必败节点。
二:当点数不超过 n n n 时定理成立, n + 1 n+1 n+1 个节点定理也成立
两种情况:
根节点有一个儿子 ( 1 ) (1) (1)
根节点不止一个儿子 ( 2 ) (2) (2)
( 1 ) : (1): (1): 假设树长这个样子:
设这棵树为 T 原 T_\text{原} T原,删边后 T T T, T 原 T_\text{原} T原 以 u u u 为根的子树为 T ′ T' T′。
删一条边,也分两种情况
- 删边 { r t , u } \{rt,u\} {rt,u},此时只剩下根节点, SG ( T ) = 0 \operatorname{SG}(T)=0 SG(T)=0
- 删 T ′ T' T′ 中的一条边 E E E,此时因为删边后点数会小于 n n n,所以此时定理成立,那么 SG ( r t ) = SG ( u ) + 1 \operatorname{SG}(rt)=\operatorname{SG}(u)+1 SG(rt)=SG(u)+1。
那 SG ( u ) \operatorname{SG}(u) SG(u) 的范围是多少呢?
设 SG ( u ) = k \operatorname{SG}(u)=k SG(u)=k,则 T ′ T' T′ 的后继局面中的 SG \operatorname{SG} SG 值必定包含了 [ 0 , k ) \left[0,k\right) [0,k) 的所有自然数,范围就是 0 ∼ k − 1 0\sim k-1 0∼k−1(因为一个局面的 SG \operatorname{SG} SG 值等于其后继局面 SG \operatorname{SG} SG 值的 mex \operatorname{mex} mex)。所以 SG ( T ) \operatorname{SG}(T) SG(T) 可以取遍 1 ∼ k 1\sim k 1∼k。
算上第一种情况,删边后 SG ( r t ) \operatorname{SG}(rt) SG(rt) 的范围是 0 ∼ k 0\sim k 0∼k,取遍了 ≤ k + 1 \le k+1 ≤k+1 的自然数,于是删边前 SG ( r t ) = k + 1 \operatorname{SG}(rt)=k+1 SG(rt)=k+1
于是 SG ( r t ) = SG ( u ) + 1 \operatorname{SG}(rt)=\operatorname{SG}(u)+1 SG(rt)=SG(u)+1 得证。
( 2 ) : (2): (2): 此时的图可以拆成若干个根节点相同但 u u u 不同的和上图相同的树,此时就和 N i m \tt{Nim} Nim 一样,异或起来即可。
Hard VERSION
最毒瘤的东西来了。。。
题面完全一样,只不过树变成了一个图。只能放定理了:
F u s i o n P r i n c i p l e \sf{F\color{red}usion\text{ }Principle} Fusion Principle 定理如下:
把图中任意一个偶长度环缩成一个新点,任意一个奇长度环缩成一个新点加一条边,所有连到原先环上的边全部改为与新点相连。这样的改动不会影响图的 SG \operatorname{SG} SG 值
证明是什么?我不会
事实上这种题考的挺偏,而且一出就是板子,在 NOI \texttt{NOI} NOI 赛场上也几乎不可能碰到。
7. 博弈点搜索+
之所以这样安排顺序,是因为这个东西有相比起普通的博弈点搜索难不止亿点点。
极大极小搜索 & a l p h a alpha alpha - b e t a beta beta 剪枝
来看一个栗子:
题目:给定一个图,图上包含一些虚边和实边,两个人轮流每次把一条虚边变成实边,这个人的得分就会加上新产生的三角形数。
Pay Attention: 这只是其中一个栗子, α \alpha α - β \beta β 剪枝可以概括为:两个人博弈,甲想让自己的利益最大化,乙想让甲的利益最小化。
分析:
这种题没有技巧,只能爆搜。但这并不影响我们在爆搜时使用技巧!!
DFS 技巧,常用的就是 \, 记忆化搜索,可行性剪枝,最优性剪枝。
记忆化显然不可能,可行性剪枝放到这里来是什么鬼((??于是考虑最优性剪枝。
假如选一个点,并且定义去这个点后甲的得分减去乙的得分为 f ( x ) f(x) f(x)。作为甲,希望它最大;作为乙,希望它最小,于是定义 max ( f ) = α , min ( f ) = β \max(f)=\alpha,\min(f)=\beta max(f)=α,min(f)=β(点题的写作手法)
再来定义甲的局面为 m a x max max 局面,乙的局面为 m i n min min 局面。
考虑这一步到甲,是 m a x max max 局面,显然这个局面会继承上一步的 β \beta β,而甲会把它走成 α \alpha α 的情况。
但是如果 α ( n o w ) > β ( p r e v ) \alpha(now)>\beta(prev) α(now)>β(prev),说明这条路线不是最优, p r e v prev prev 的其它后继状态也可以不用再搜了。
为什么?说白点,这个局面满足以下条件之一
乙想让甲得分更小,甲却得到了比乙给甲的最小得分还大的得分;这时乙本可以给甲更小的得分,原来的方案不是最优方案。
又或者甲想让自己得分尽量大,却给乙一个比甲现在得分要少的局面;显然这让自己得分更少,原来的方案也不是最优方案。
两个不都是一样的吗
但是这种东西你需要两方都去考虑 虽然让自己利益最大就等于让别人利益最小
经过剪枝,最后剩下的都是最优决策。
于是这种搜索叫极大极小搜索,这种剪枝叫做 a l p h a alpha alpha - b e t a beta beta 剪枝。(呼应开头,画龙点睛)
8. 总结
博弈论这种东西,可以分类为:
- 巴什博弈、威佐夫博弈、 N i m \tt Nim Nim 以及 Anti-Nim \texttt{Anti-Nim} Anti-Nim ,这种东西有 O ( 1 ) O(1) O(1) 公式,其余大部分都没有
- 用到 SG \operatorname{SG} SG 函数的游戏(包括大部分 N i m \tt Nim Nim 拓展):运用 SG \operatorname{SG} SG 函数 DFS 计算方法,牢记 mex \operatorname{mex} mex。
- 博弈点搜索以及其拓展 极大极小搜索 :前者有可能用到,记忆化搜索,后者有专门的最优性剪枝