對於一些博弈問題的總結

可能是自己做的題目不夠多把,自己目前對博弈題的認識是:只要是符合nim規則的博弈,都可以用sg函數求解,但是如果我們只考慮單個遊戲,不考慮遊戲之間的組合,只考慮必敗態和必勝態就好了,自己能力有限,目前對這個結論只有一個感性上的認識,因爲自己對sg函數的認識只停留在mex操作這個低級的層面,雖然維基百科上有sg相關的證明,但是自己英語的閱讀能力太差,又忙於訓練,沒有太多的時間。希望大家原諒我不能嚴謹地證明這個結論,也希望大家能對我提出各種意見。

組合遊戲

組合遊戲必須使用sg函數,因爲各個獨立的遊戲之間的必敗態和必勝態之間已經失去了關聯性,無法單純地用P,N這兩個簡單的符號表示,而是要利用sg函數以及各個獨立遊戲sg值的異或和來深層的挖掘目前狀態的性質。網上有很多sg函數相關的入門文章,但我覺得這些文章大多都是點到即止,如果你想深入地瞭解sg函數的相關原理,應該選擇直接閱讀相關論文,如果你只是想可以成功地切幾個題,恕我愚昧的言論:只要記結論就夠了。(我不是不鼓勵大家去了解背後的原理,我只是想說,在我個人的理解中,如果精力有限,sg函數是無法很好理解的,淺嘗輒止的話,反而更不好)

在一些大數據的題目裏,sg函數的計算會直接導致TLE,這時就要通過本地打一些小數據的表,來試圖找到題目裏的規律,當然,有些題目我們可以通過自己分析來得到解題需要的規律或者結論,是通過分析還是通過打表得出,就是仁者見仁智者見智了。

丟一道昨天做到的利用sg函數的博弈題(只是昨天做到而已,沒有代表性,但依然可以做做熟悉下sg函數):http://acm.tju.edu.cn/toj/showp4172.html
自己的代碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define MS(x, y) memset(x, y, sizeof(x))

const int MAXN = 1e3 + 5;

int n;
int sg[MAXN];
bool sgVis[MAXN];
char str[MAXN];

int main() {
  for (int i = 2; i < MAXN; ++i) {
    for (int j = 0; j <= i - 2; ++j) {
      sgVis[sg[j] ^ sg[i - 2 - j]] = true;
    }
    for (int j = 0; j < MAXN; ++j) if (!sgVis[j]) {
      sg[i] = j;
      break;
    }
    for (int j = 0; j <= i - 2; ++j) {
      sgVis[sg[j] ^ sg[i - 2 - j]] = false;
    }
//    cout << i << ": " << sg[i] << endl;
  }
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%s", &n, str);
    int cnt = 0, ans = 0;
    for (int i = 0; str[i]; ++i) {
      if (str[i] == '0') {
        ans ^= sg[cnt];
        cnt = 0;
      } else ++cnt;
    }
    ans ^= sg[cnt];
    puts(ans ? "Alice" : "Bob");
  }
}

非組合遊戲

其實就是隻有一個遊戲的意思了,這種題我們可以不依靠sg函數,直接通過必敗點和必勝點的判斷來找到解題的方法。這樣少了mex操作,可以節省時間,自己思考的時候因爲只有P和N兩種狀態,思考起來比sg值更直觀。

丟一道最近做過的題:http://acmoj.shu.edu.cn/problem/418/,這題直接通過N點P點打表就好了,即:必敗點的前驅一定是必勝點,不需要考慮sg函數。

代碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int MAXN = 500 + 5;

int n, m;

bool notprime[MAXN];
int prime[MAXN], pnum;
bool win[MAXN][MAXN];

int main() {
  for (int i = 2; i < MAXN; ++i) if (!notprime[i]) {
    prime[pnum++] = i;
    for (int j = i * 2; j < MAXN; j += i) notprime[j] = true;
  }
  for (int i = 1; i < MAXN; ++i) for (int j = 1; j < MAXN; ++j) if (!win[i][j]) {
    for (int k = 0; k < pnum; ++k) {
      if (i + prime[k] < MAXN) win[i + prime[k]][j] = true;
      if (j + prime[k] < MAXN) win[i][j + prime[k]] = true;
      if (i + prime[k] < MAXN && j + prime[k] < MAXN) win[i + prime[k]][j + prime[k]] = true;
    }
  }
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%d", &n, &m);
    if (win[n][m]) puts("Sora");
    else puts("Shiro");
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章