博弈论
定义:有若干个人进行博弈,每人轮流操作,且每个人的每一步都是最正确的操作,问什么时候必败/必胜
模型:
建模
①设先手必胜局面为 N N N,先手必败局面为 P P P,于是就有了一些结论
- 如果当前局面为 N N N,那么它肯定变化成 P P P,因为我们要必胜,那么对手必须进入一先手必败的 P P P中,于是 N N N必定能变化成 P P P
- 同理,一个 P P P肯定能变化成 N N N
②于是我们就可以开始建图了
- 设每一个状态都为一个节点,我们就可以从当前节点像每一个能到达的点,连一条有向边
- 于是我们已知游戏结束的状态是 P P P,然后从这个状态扩展出去,如果一个节点能到底 P P P那么它就是 N N N, 如果能到到达的每一个点都是 N N N,即不能到底 P P P,那么则为 P P P
③我们的博弈论基本模型就建好了,其实每一个博弈论问题都可以转换为一个 D A G DAG DAG图
S G SG SG定理和 S G SG SG函数
m e x mex mex运算:
对于一个集合 S S S,它的 m e x ( S ) mex(S) mex(S)就为最小的、不属于集合 S S S的非负整数
S G ( x ) SG(x) SG(x):
对于 S G ( x ) SG(x) SG(x),有 S G ( x ) = m e x ( S G ( y ) , < x , y > ∈ E ) SG(x)=mex(SG(y),<x,y>∈E) SG(x)=mex(SG(y),<x,y>∈E),其实就是它在 D A G DAG DAG图中所有后继状态的 m e x mex mex值
S G ( x ) SG(x) SG(x)的拆分
如果 x x x可以拆分成互不影响的几个子游戏 x 1 , x 2 , . . . x n x_1,x_2,...x_n x1,x2,...xn,那么 S G ( x ) = S G ( x 1 ) ⊕ S G ( x 2 ) ⊕ . . . ⊕ S G ( x n ) SG(x)=SG(x_1)⊕SG(x_2)⊕...⊕SG(x_n) SG(x)=SG(x1)⊕SG(x2)⊕...⊕SG(xn)
S G ( x ) SG(x) SG(x)与胜负
S G ( x ) = 0 SG(x)=0 SG(x)=0为先手必败,否则先手必胜
常见模型
巴什博奕( B a s h G a m e Bash Game BashGame)
题目大意:有 n n n个石子,每次少取 1 1 1个,最多取 m m m个,问先手必胜还是必败
分析:(正常分析)
- 假设我们面对了一个 ( m + 1 ) ∣ n (m+1)|n (m+1)∣n的情况,那么假设我们取了 x x x个,那么对手肯定能取 m + 1 − x m+1-x m+1−x个,使得对手 + + +我们的和为 m + 1 m+1 m+1,因为 ( m + 1 ) ∣ n (m+1)|n (m+1)∣n,所以对手肯定能取到最后一个,所以当 ( m + 1 ) ∣ n (m+1)|n (m+1)∣n时,先手必败
- 同理如果 ( m + 1 ) ∤ n (m+1) \nmid n (m+1)∤n,则我们可以先取走 n % ( m + 1 ) n\%(m+1) n%(m+1)个,使得对手面临 ( m + 1 ) ∣ n (m+1)|n (m+1)∣n,那么这时先手必胜
代码实现:
#include<bits/stdc++.h>
using namespace std;
int t;
int main()
{
cin >> t;
while (t--)
{
int n,k;cin >> n >> k;
if (n%(k+1)==0) printf("B\n");
else printf("A\n");
}
}
威佐夫博弈( W y t h o f f G a m e Wythoff Game WythoffGame)
题目大意:有两堆石头,数量分别为 n , m n,m n,m,每次可以在一堆,或者两堆中,同时取任意个石头,不能不取,问先手必胜还是必败
分析:(打标,找规律)
- 我们列举前几个先手必败的情况看一些, ( 0 , 0 ) , ( 1 , 2 ) , ( 3 , 5 ) , ( 4 , 7 ) , ( 6 , 10 ) . . . (0,0),(1,2),(3,5),(4,7),(6,10)... (0,0),(1,2),(3,5),(4,7),(6,10)...,对于以 n n n排序之后的数对,我们发现 n i n_i ni为之前所有数对中未出现的最小自然数, m i m_i mi为 n i + i − 1 n_i+i-1 ni+i−1,
- 其实我们就可以暴力算出当前局面是什么了,但有大佬又证明出了,每个先手必败的局面的数对都是 ( [ i × ϕ ] , [ i × ϕ 2 ] ) ([i×ϕ],[i×ϕ^2]) ([i×ϕ],[i×ϕ2]), ϕ = 5 − 1 2 ϕ=\frac{\sqrt5-1}{2} ϕ=25−1(证明)(%%%)
代码实现:
#include<iostream>
#include<cmath>
using namespace std;
const double phi=(sqrt(5)+1)/2;
int a,b,t;
int main()
{
cin >> t;
ios::sync_with_stdio(false);
while (t--)
{
cin >> a >> b;
if (a>b) swap(a,b);
int A=abs(a-b)*phi;
if (A==a) cout << "B"<< endl;
else cout << "A" << endl;
}
return 0;
}
尼姆博弈( N i m G a m e Nim Game NimGame)
题目大意:有 n n n堆石子,每堆石子有 A i A_i Ai个石子,两个人轮流操作,每次从一堆石子中,取任意个,不能不取,问先手必胜还是必败
分析:
- 设 A 1 ⊕ A 2 ⊕ . . . ⊕ A n ≠ 0 A_1 ⊕ A_2 ⊕ ... ⊕ A_n\not=0 A1⊕A2⊕...⊕An=0,那么必然有 A i ⩾ x A_i \geqslant x Ai⩾x(异或的性质),所有说只要将 A i A_i Ai减成 x x x,那么就可以使得 A 1 ⊕ A 2 ⊕ . . . ⊕ A n = 0 A_1 ⊕ A_2 ⊕ ... ⊕ A_n=0 A1⊕A2⊕...⊕An=0,于是一个异或和不为 0 0 0的一定可以到底一个异或和为 0 0 0
- 设 A 1 ⊕ A 2 ⊕ . . . ⊕ A n = 0 A_1 ⊕ A_2 ⊕ ... ⊕ A_n=0 A1⊕A2⊕...⊕An=0,如果可以将 A i A_i Ai变成 A i ′ A_i^{'} Ai′使得 A 1 ⊕ A 2 ⊕ . . . ⊕ A n ≠ 0 A_1 ⊕ A_2 ⊕ ... ⊕ A_n\not=0 A1⊕A2⊕...⊕An=0,那么将两个式子异或,得到 A i ⊕ A i ′ = 0 , A i = A i ′ A_i ⊕ A_i^{'}=0,A_i=A_i^{'} Ai⊕Ai′=0,Ai=Ai′,矛盾,所以异或和为 0 0 0的一定不可以保持异或和为 0 0 0
- 因为所有石子都被取完时为 P P P,而且石头是不断减少的,所以当异或和为 0 0 0是先手必败,否则先手必胜
代码实现:
#include<bits/stdc++.h>
using namespace std;
int a[10010],n,t;
int main()
{
cin >> t;
while (t--)
{
int ans;
cin >> n;
for (int i=1;i<=n;i++) cin >> a[i];
ans=a[1];
for (int i=2;i<=n;i++) ans=ans^a[i];
if (ans==0) printf("No\n");
else printf("Yes\n");
}
}