POJ 2311 Cutting Game 博弈論 SG 函式的理解
阿新 • • 發佈:2018-12-02
這題是對 SG 函式本質理解的一個絕佳的題目
之前我們做題的時候大多都是 Nim 博弈,想當然的認為 SG 函式用二進位制數表示一個局面用的是棋子個數,並且也只適用於這個局面,甚至認為 SG 函式只能解決 Nim 類的題目,並且想當然的認為最初狀態(即0的時候) SG = 0
實際上,SG 函式的適用範圍遠遠比此廣得多。而且 SG 函式用來表示局面的二進位制只要合理能夠區分局面,並且能夠自圓其說的轉移狀態,那麼 SG 函式就是正確的。
本題中,用了二維的 SG 來表示當前局面,並且推的過程中充分考慮了題意來尋找最終狀態
SG 函式應該在最終狀態(即無法操作態)返回 0, 這題中,如果僅僅認為是 W == H == 1
此外 SG 函式不能簡單的將贏的局面返回 1 。比如你手裡是 7*1 的格子你就贏了,但是你直接返回1是不妥的。SG 函式只有必敗態才是確定的 0 ,其餘狀態都是異或出來的一個非零值,不一定是 1,如果你將贏的局面粗暴的賦值為 1,可能會對其他的異或判斷產生影響,因此不能對贏的局面簡單的賦值 1
本題的結束狀態既然不是 W == H == 1
,那麼是什麼呢?既然不方便判斷,我們可以將這個最終狀態往上層再拉一步。你發現,當你手裡的局面是 22, 23, 32, 33 的時候你是必輸的,其餘情況則不能一眼看出來需要進一步判斷。那麼就可以將這四個當成必敗的最終狀態。並且根據必敗的最終狀態的 SG 值為 0 的規則對這四個狀態賦值為 0 。這實際上是將最終態上拉到了一個可以拆分成獨立子游戲的地方。
到這裡總結一下:
- SG 函式表示不必拘泥於二進位制形式和 Nim 的關係
- 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;
}
}
}