組合賽局
北一女中 鄭允臻
自我介紹
- 北一女中高三
- 各個OJ handle:hhhhaura (可能有4~20個h)
- 因為之前欠8e7人情,所以今天來還債
- 裝弱是強者的特權,所以我沒資格裝弱
WARNING!
- 內容98.87%抄襲2022 IOICamp的講義
- 譴責劉澈造謠,我沒有要講hackenbush
今天希望你能夠學會
組合賽局基本介紹
PART ONE
什麼是組合賽局?
- 賽局理論的一個分支
- 兩位玩家對戰,雙方輪流操作
- 資訊完全公開
- 操作與結果具有決定性(不含隨機成份)
- 結果:輸/贏/平手
Game Graph
DAG ➡ 動態規劃
組合賽局分類
- 依結構分類
- 依玩法分類
依結構分類
| 玩家合法操作異同 | 有偏(Partial) | 無偏(Impartial) |
| 是否有限步內結束 | 無環(Loopfree) | 有環(Loopy) |
互為補集
依玩法分類
- 標準(Normal):輪到該回合時,無法操作者輸
- 匱乏(Misère):輪到該回合時,無法操作者贏
並沒有構成所有賽局!
競程裡面遇到的賽局
八成以上都是標準無偏無環賽局!
對於一個組合賽局:
- 先手有必勝策略
- 後手有必勝策略
- 雙方皆有必勝策略
考慮無環賽局
- 標準無環賽局無平手
- 匱乏無環賽局無平手
將遊戲規則壓縮進狀態
無偏無環賽局Game graph
關於有偏賽局的Game Graph
在節點上多一個維度!
(0, A)
(1, B)
(1, A)
(0, C)
(1, D)
(0, E)
是DAG的話又可以DP了?
關於匱乏賽局的Game Graph
把原圖上的終點再連出去一個虛點!
A
C
B
D
虛
便得跟標準賽局一樣了?
欸?那幹麻要分那麼多類!
不是就多定義一個維度轉換成圖就好了嗎?
不用賽局理論就可以做的賽局題?
標準無偏無環賽局1
PART TWO
賽局和、型別、等價賽局

The Subtraction Game
有N顆石頭,兩人輪流拿,每次可以拿走1 ~ 3顆,拿到最後一顆的一方贏。請問先手還後手必勝?
- 無偏:每個人的操作相同
- 無環:石頭必定在有限次數拿完
怎麼做?
怎麼做?
| x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| S(x) | -1 | +1 | +1 | +1 | -1 | +1 | +1 | +1 | -1 |
有規律!
怎麼做?
S(x) = 1 時:
讓輪到對方時剩下4k顆石頭
賽局合
- G1 + G2
- 每次選其中一個賽局操作
- eg. 西洋棋 + 象棋
當遊戲規則壓縮遇到賽局合
以有偏賽局為例:
- 兩個賽局 → (A, B, 0/1)
- 三個賽局 → (A, B, C, 0/ 1)
- N個賽局 → (A, B, C, ....., 0/ 1)
太複雜了吧?!
因此才要把賽局分類探討
(無偏無環)型別
- N(Next player win)先手獲勝
- P(Previous player win)後手獲勝
如果一個賽局盤面可以轉移到型別為P的盤面,代表他的型別是N;反之,如果他只可以轉移到型別為N的盤面,代表他的型別是P。
等價賽局
給定兩個賽局G1, G2,如果對於任意賽局H,G1+H與G2+H的型別皆相同,那麼我們稱這兩個賽局等價G1=G2。
- 不要求兩個賽局可以轉移到的賽局必須等價
- 所有賽局都可以轉換成任一等價賽局
若G2的型別是P,那麼G1+G2=G1
- 本來在G1有優勢的玩家看見對方玩G2→去玩G2
- G1的優勢玩家能夠保持優勢→G2不影響型別
若G1、G2都是P,則G1=G2
- 對任何一個賽局H都有H+G1=H=H+G2,因此G1=G2。
- 所有型別為P的賽局都是同一種賽局(零賽局)
標準無偏無環賽局2
PART THREE
酷酷定理:NIM、SG Theorem
NIM game
有N堆石頭,分別是a1,a2,...,aN顆,兩人輪流拿,每次可以從其中一堆拿走任意堆但是至少一顆石頭,無法拿的人就輸了,請問先手必勝還是後手必勝。
好像很難...
特徵值
Bouton's Theorem
此遊戲先手必勝(型別N),若且唯若X ≠ 0。同理,此遊戲後手必勝(型別P),若且唯若X = 0。
為什麼?
若能說明以下兩件事,則可證明此定理:
- X=0的狀態無法走到其他X=0的狀態,且對於結束局面,X=0
- X≠0的狀態必定可以走到一個X=0的狀態。
X=0的狀態無法走到其他X=0的狀態,且對於結束局面,X=0
後半部:trivial
前半部:
X≠0的狀態必定可以走到一個X=0的狀態
目前特徵值為X的最高bit為b
則必定有至少一個ai有b以上的bit
因此將ai⊕X必定<ai
把ai拿掉ai-(ai⊕X)顆石頭
則X′=X⊕ai⊕(ai⊕X)=0
故得證。
NIM sum
特徵值為X的Nim遊戲G,等價於僅有一堆數量為X的Nim遊戲H
打打字?
Sprague-Grundy Theorem
標準無偏賽局的終級定理!
對於任一一個標準無偏賽局,必定有一個與其等價的一堆Nim遊戲,而此堆Nim遊戲的大小,稱作SG−Value。
Chose NIM
給定一個N個元素的集合
先手從中挑選出一個數字 ,接下來便成了一個僅有一堆數量 的Nim遊戲。此時由後手取石子,接著依照Nim遊戲的規則繼續遊玩。
MEX principle
給定一個集合s={a1,a2,...,aN},若從中選擇一堆玩僅有一堆數量為ai的Nim遊戲G(Choose Nim),那麼此賽局會等價於一堆數量為mex(s)的Nim遊戲H。
MEX:集合中最小沒有出現的非負整數
為什麼莫名其妙就跟MEX扯上關係了呢?
先說明G + H = 0
令b=mex(s)
若是先手選擇G並選擇了一堆石頭c那麼c≠b ,則X = b⊕c≠0,先手必敗。
先說明G + H = 0
若先手選擇H則b' < b:根據mex的定義,後手必定可以選擇一堆石頭c=b′,此時特徵值為0,先手必敗。
等號兩邊都加上H→G+(H+H)=H
標準無偏無環賽局等價Nim
說明一個盤面必定等價一堆Nim
標準無偏無環賽局等價Nim
Game Graph上沒有出度的點→P型別
標準無偏無環賽局等價Nim
數學歸納法:
Mex principle→H=*m, m = mex(H)
標準無偏無環賽局等價Nim
只要說明G=H就可以了!
(G + H = 0)
標準無偏無環賽局等價Nim
G + H = 0
無論先手動G或H
後手都可以動另外一個遊戲使特徵值為0
結論:
- 一個標準無偏無環賽局等價於一堆Nim
- 用Nim sum求賽局和
- 在game graph上用mex principle轉移
你和你的朋友在玩一個遊戲。你們一共有N條紙帶,每條紙帶有 格,每次你們輪流選一條紙帶上的一個空白格畫上圈圈,畫出三個連續圈圈的那個人就贏了(不需要三個都由他來畫)。在雙方都是絕頂聰明的情況下,問你結果會是先手贏、後手贏,或是平手?
剛剛說無偏無環必定沒有平手?
難道這不是無偏無環?
有一個M * N的棋盤,每個格子是黑色或白色。兩個人輪流行動,每次行動可以選擇一個白色的格子以及一個以他為左下角的矩形,並將這矩形的格子顏色翻轉。無法行動者輸,請問先手還是後手必勝?若先手必勝,請輸出必勝的第一步有幾種?字典序最小的解?
可以作為左上角的格子數遞減➡遊戲必定會結束
把每一格想成一個遊戲(i, j)
每個操作都不獨立欸?怎麼辦?
把Xor操作變成加減法
-1
+1
+1
+1
+1
+1
+1
+1
在白色格子裡加一個賽局(賽局合)
加到2➡ 該格特徵值為零
➡跟Xor的結果一樣!
DP出每一格的SGV
O(N*M)種轉移
O(NNMM)
- 字典序和方法數自己想
- T到400➡要預處理
namespace solver {
int n, m;
const int N = 100, M = 100;
vector<vector<int>> mp, dp, pre;
vector<vector<tuple<int, int, int, int>>> v;
void init_(int _n, int _m) {
n = _n, m = _m;
mp.assign(n + 1, vector<int>(m + 1, 0));
}
void build() {
pre.assign(N + 1, vector<int>(M + 1, 0));
dp.assign(N + 1, vector<int>(M + 1, 0));
v.assign(2ll * N * M + 1, vector<tuple<int, int, int, int>>());
rep(i, 1, N) rep(j, 1, M) {
vector<int> cnt(i * j + 1, 0);
pre[i][j] = pre[i - 1][j] ^ pre[i][j - 1]
^ pre[i - 1][j - 1];
rep(a, 1, i) rep(b, 1, j) {
int val = pre[i][j] ^ pre[a - 1][j]
^ pre[i][b - 1] ^ pre[a - 1][b - 1];
if(val <= i * j) cnt[val] ++;
}
rep(a, 0, i * j) if(!cnt[a]) {
dp[i][j] = a;
break;
}
rep(a, 1, i) rep(b, 1, j) {
int val = pre[i][j] ^ pre[a - 1][j]
^ pre[i][b - 1] ^ pre[a - 1][b - 1];
int to = val ^ dp[i][j];
v[to].push_back({i, j, a, b});
}
pre[i][j] ^= dp[i][j];
}
}
void solve() {
int ans = 0;
rep(i, 1, n) rep(j, 1, m) {
if(mp[i][j]) ans ^= dp[i][j];
}
if(!ans) cout << "No\n";
else {
tuple<int, int, int, int> best = {INF, INF, INF, INF};
int cnt = 0;
for(auto i : v[ans]) {
int a, b, c, d;
tie(a, b, c, d) = i;
if(a > n || b > m) continue;
if(!mp[a][b]) continue;
cnt ++;
best = min(best, {n-a+1, m-b+1, n-c+1, m-d+1});
}
int a, b, c, d;
tie(a, b, c, d) = best;
cout << cnt << " " << a << " ";
cout << b << " " << c << " " << d << "\n";
}
}
};
using namespace solver;
signed main() {
ios::sync_with_stdio(false), cin.tie(0);
int n, m; build();
while(cin >> n >> m) {
init_(n, m);
rep(i, 1, n) rep(j, 1, m) {
int x; cin >> x;
mp[n - i + 1][m - j + 1] = x;
}
solve();
}
return 0;
}
參考CODE
標準無偏有環賽局
PART FOUR
再探型別
D
N
P
N
P
新型別:D(Draw)平手!
沒有拓樸順序,要怎麼判斷型別阿?
勝負判斷
實做就在定義裡...
- 從確定的點逆推
- 沒有出度的點→型別為P
- 可以連到P的點→型別為N
- 只能連到N的點→型別為P
- 未定義→型別為D
Wiwi和郝拉在玩遊戲。有一個N個單字的集合S,由Wiwi先說出一個英文單字a,而郝拉要說出S中前三個字元與a的後三個字元相同的單字,依此類推。
eg. purple ➡ please➡ase....
一個單字可以被用多次,不能說出單字的人輸。
當然是Wiwi會贏阿!?
淺談匱乏賽局
PART FIVE
當每堆石頭都是0或1時,
先手必勝若且唯若特徵值是0
觀察
恰有一堆石頭大於二則先手必勝
觀察
超過一堆石頭大於二,
- 若特徵值非零→剩最後一堆超過二的石頭時必輪到先手(N)
- 若特徵值為零→剩最後一堆超過二的石頭時必輪到後手(P)
觀察
- 每堆石頭數量<2 :特徵值為1則先手必敗
- 否則特徵值為1則先手必勝
結論
一些題目們
APPENDIX
想研究更多的話...
- ON NUMBERS AND GAMES
- Game Theory : A Playful Introduction
- Combinatorial Game Theory
- 去找劉澈教你怎麼拿到PDF
combinatorial games
By Yun-Chen Cheng
combinatorial games
- 175