1. 程式人生 > >POJ 2311 Cutting Game 博弈論 SG 函式的理解

POJ 2311 Cutting Game 博弈論 SG 函式的理解

傳送門

這題是對 SG 函式本質理解的一個絕佳的題目
之前我們做題的時候大多都是 Nim 博弈,想當然的認為 SG 函式用二進位制數表示一個局面用的是棋子個數,並且也只適用於這個局面,甚至認為 SG 函式只能解決 Nim 類的題目,並且想當然的認為最初狀態(即0的時候) SG = 0

實際上,SG 函式的適用範圍遠遠比此廣得多。而且 SG 函式用來表示局面的二進位制只要合理能夠區分局面,並且能夠自圓其說的轉移狀態,那麼 SG 函式就是正確的。

本題中,用了二維的 SG 來表示當前局面,並且推的過程中充分考慮了題意來尋找最終狀態

SG 函式應該在最終狀態(即無法操作態)返回 0, 這題中,如果僅僅認為是 W == H == 1

的時候是最終失敗態就錯了。 實際上,當你手中拿著一個 n1 (n >= 2)的格子的時候,剪一刀你就贏了,而對手這個時候手裡是 m1 (m >=1),如果你寫的迴圈是從 1 開始列舉後繼狀態並將他們異或的就錯了,因為這個時候已經無法操作了,你卻認為可以繼續分解。換句話說,你不能將當前局面拆分成獨立的子游戲了,因為你這一步已經贏了。不獨立的子游戲之間不能隨便用局面分解原理了。

此外 SG 函式不能簡單的將贏的局面返回 1 。比如你手裡是 7*1 的格子你就贏了,但是你直接返回1是不妥的。SG 函式只有必敗態才是確定的 0 ,其餘狀態都是異或出來的一個非零值,不一定是 1,如果你將贏的局面粗暴的賦值為 1,可能會對其他的異或判斷產生影響,因此不能對贏的局面簡單的賦值 1

本題的結束狀態既然不是 W == H == 1 ,那麼是什麼呢?既然不方便判斷,我們可以將這個最終狀態往上層再拉一步。你發現,當你手裡的局面是 22, 23, 32, 33 的時候你是必輸的,其餘情況則不能一眼看出來需要進一步判斷。那麼就可以將這四個當成必敗的最終狀態。並且根據必敗的最終狀態的 SG 值為 0 的規則對這四個狀態賦值為 0 。這實際上是將最終態上拉到了一個可以拆分成獨立子游戲的地方。

到這裡總結一下:

  1. SG 函式表示不必拘泥於二進位制形式和 Nim 的關係
  2. SG 函式最底層的值是無法繼續操作的最終步驟,將他們賦值為 0, 而不是簡單的 0 的情況
#include
<iostream>
#include <cstring> using namespace std; const int maxn = 2e2+5; const int maxl = 1e3+50; int sg[maxn][maxn]; int fun(int W, int H) { if (sg[W][H] > -1) return sg[W][H]; bool vis[maxl]; memset(vis, false, sizeof(vis)); for (int i = 2; i < W-1; ++i) { vis[fun(i, H)^fun(W-i, H)] = true; } for (int i = 2; i < H-1; ++i) { vis[fun(W, i)^fun(W, H-i)] = true; } for (int i = 0; ; ++i) { if (!vis[i]) { sg[W][H] = i; return sg[W][H]; } } } int main() { int W, H; memset(sg, -1, sizeof(sg)); while (cin >> W >> H) { if (fun(W, H)) { cout << "WIN" << endl; } else { cout << "LOSE" << endl; } } }