博弈論
定義:有若干個人進行博弈,每人輪流操作,且每個人的每一步都是最正確的操作,問什么時候必敗/必勝
模型:
建模
①設先手必勝局面為\(N\),先手必敗局面為\(P\),於是就有了一些結論
- 如果當前局面為\(N\),那么它肯定變化成\(P\),因為我們要必勝,那么對手必須進入一先手必敗的\(P\)中,於是\(N\)必定能變化成\(P\)
- 同理,一個\(P\)肯定能變化成\(N\)
②於是我們就可以開始建圖了
- 設每一個狀態都為一個節點,我們就可以從當前節點像每一個能到達的點,連一條有向邊
- 於是我們已知游戲結束的狀態是\(P\),然后從這個狀態擴展出去,如果一個節點能到底\(P\)那么它就是\(N\), 如果能到到達的每一個點都是\(N\),即不能到底\(P\),那么則為\(P\)
③我們的博弈論基本模型就建好了,其實每一個博弈論問題都可以轉換為一個\(DAG\)圖
\(SG\)定理和\(SG\)函數
\(mex\)運算:
對於一個集合\(S\),它的\(mex(S)\)就為最小的、不屬於集合\(S\)的非負整數
\(SG(x)\):
對於\(SG(x)\),有\(SG(x)=mex(SG(y),<x,y>∈E)\),其實就是它在\(DAG\)圖中所有后繼狀態的\(mex\)值
\(SG(x)\)的拆分
如果\(x\)可以拆分成互不影響的幾個子游戲\(x_1,x_2,...x_n\),那么\(SG(x)=SG(x_1)⊕SG(x_2)⊕...⊕SG(x_n)\)
\(SG(x)\)與勝負
\(SG(x)=0\)為先手必敗,否則先手必勝
常見模型
巴什博奕(\(Bash Game\))
題目大意:有\(n\)個石子,每次少取\(1\)個,最多取\(m\)個,問先手必勝還是必敗
分析:(正常分析)
- 假設我們面對了一個\((m+1)|n\)的情況,那么假設我們取了\(x\)個,那么對手肯定能取\(m+1-x\)個,使得對手\(+\)我們的和為\(m+1\),因為\((m+1)|n\),所以對手肯定能取到最后一個,所以當\((m+1)|n\)時,先手必敗
- 同理如果\((m+1) \nmid n\),則我們可以先取走\(n\%(m+1)\)個,使得對手面臨\((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");
}
}
威佐夫博弈(\(Wythoff Game\))
題目大意:有兩堆石頭,數量分別為\(n,m\),每次可以在一堆,或者兩堆中,同時取任意個石頭,不能不取,問先手必勝還是必敗
分析:(打標,找規律)
- 我們列舉前幾個先手必敗的情況看一些,\((0,0),(1,2),(3,5),(4,7),(6,10)...\),對於以\(n\)排序之后的數對,我們發現\(n_i\)為之前所有數對中未出現的最小自然數,\(m_i\)為\(n_i+i-1\),
- 其實我們就可以暴力算出當前局面是什么了,但有大佬又證明出了,每個先手必敗的局面的數對都是\(([i×ϕ],[i×ϕ^2])\),\(ϕ=\frac{\sqrt5-1}{2}\)(證明)(%%%)
代碼實現:
#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;
}
尼姆博弈(\(Nim Game\))
題目大意:有\(n\)堆石子,每堆石子有\(A_i\)個石子,兩個人輪流操作,每次從一堆石子中,取任意個,不能不取,問先手必勝還是必敗
分析:
- 設\(A_1 ⊕ A_2 ⊕ ... ⊕ A_n\not=0\),那么必然有\(A_i \geqslant x\)(異或的性質),所有說只要將\(A_i\)減成\(x\),那么就可以使得\(A_1 ⊕ A_2 ⊕ ... ⊕ A_n=0\),於是一個異或和不為\(0\)的一定可以到底一個異或和為\(0\)
- 設\(A_1 ⊕ A_2 ⊕ ... ⊕ A_n=0\),如果可以將\(A_i\)變成\(A_i^{'}\)使得\(A_1 ⊕ A_2 ⊕ ... ⊕ A_n\not=0\),那么將兩個式子異或,得到\(A_i ⊕ A_i^{'}=0,A_i=A_i^{'}\),矛盾,所以異或和為\(0\)的一定不可以保持異或和為\(0\)
- 因為所有石子都被取完時為\(P\),而且石頭是不斷減少的,所以當異或和為\(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");
}
}