SG函數


SG函數

有個講得不錯的博客:http://blog.csdn.net/strangedbly/article/details/51137432

簡單介紹:

Sprague-Grundy定理(SG定理):

游戲和的SG函數等於各個游戲SG函數的Nim和。這樣就可以將每一個子游戲分而治之,從而簡化了問題。而Bouton定理就是Sprague-Grundy定理在Nim游戲中的直接應用,因為單堆的Nim游戲 SG函數滿足 SG(x) = x。

SG函數:

首先定義mex(minimal excludant)運算,這是施加於一個集合的運算,表示最小的不屬於這個集合的非負整數。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

對於任意狀態 x , 定義 SG(x) = mex(S),其中 S 是 x 后繼狀態的SG函數值的集合。如 x 有三個后繼狀態分別為 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 這樣 集合S 的終態必然是空集,所以SG函數的終態為 SG(x) = 0,當且僅當 x 為必敗點P時。

 

解題模型:

1. 把原游戲分解成多個獨立的子游戲,則原游戲的SG函數值是它的所有子游戲的SG函數值的異或。

       即sg(G) = sg(G1) ^ sg(G2) ^ ... ^ sg(Gn)

2. 分別考慮每一個子游戲,計算其SG值。

3. 若sg(G)=0,則為P-Position(必敗態),否則為N-Position(必勝態)。

 

SG值的計算方法:(重點)

1.可選步數為1~m的連續整數,直接取模即可,SG(x) = x % (m+1);

2.可選步數為任意步,SG(x) = x;

3.可選步數為一系列不一定連續的數,用模板計算(模板在下面模板題代碼中)。

 

模板題:HDU 1536 -- S-Nim

AC代碼+詳細注釋:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #define MAXN 10010  // 最大堆數
 5 #define MAXM 110    // 最多有MAXM種不同個數的取石子方法
 6 using namespace std;
 7 int f[MAXM];   // f為可取石子數的集合
 8 int sg[MAXN];  // sg[i]表示石子數為i時的sg函數值
 9 bool Hash[MAXN];  // 標記一個數是否在mex{}集合中出現
10 // 打表預處理sg數組
11 void getSG(int m) {
12     memset(sg, 0, sizeof(sg));
13     for (int i = 1; i < MAXN; i++) {
14         memset(Hash, false, sizeof(Hash));
15         for (int j = 0; j < m && f[j] <= i; j++)
16             Hash[sg[i-f[j]]] = true;  // 當前石子數為i,i-f[i]表示由i所能達到的石子數,將其sg值標記為已出現
17         for (int j = 0; j < MAXN; j++) {  // mex(minimal excludant)運算
18             if (!Hash[j]) {
19                 sg[i] = j;
20                 break;
21             }
22         }
23     }
24 }
25 // 加一個dfs預處理sg數組,注意sg數組需要初始化為-1,而上面打表解法需要初始化為0
26 // 一般首選打表預處理,難以打表才用dfs
27 int SG_dfs(int x) {
28     if (sg[x] != -1) return sg[x];
29     memset(Hash, false, sizeof(Hash));
30     for (int i = 0; i < m && f[i] <= x; i++) { // m為集合f的大小
31         SG_dfs(x - f[i]);
32         Hash[sg[x-f[i]]] = true;
33     }
34     for (int i = 0; i < MAXN; i++) {
35         if (!Hash[i]) {
36             return sg[x] = i;
37         }
38     }
39 }
40 
41 int main() {
42     int n, m;
43     while (cin >> m && m) {
44         for (int i = 0; i < m; i++) cin >> f[i];
45         sort(f, f + m);
46         getSG(m);
47         cin >> n;
48         while (n--) {
49             int num, sum = 0;
50             cin >> num;
51             for (int i = 0; i < num; i++) {
52                 int each; cin >> each;
53                 sum ^= sg[each];
54             }
55             if (sum) cout << 'W';
56             else cout << 'L';
57         }
58         cout << endl;
59     }
60     return 0;
61 }
View Code

HDU 1517 -- A Multiplication Game (典型的有向圖游戲):

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <vector>
 4 #include <map>
 5 using namespace std;
 6 typedef long long LL;
 7 map<LL, int> mp;
 8 LL n;
 9 
10 int sg(LL x) {
11     if (x >= n) return 0;
12     if (mp.count(x)) return mp[x];
13     vector<LL> mex;
14     for (int i = 2; i <= 9; i++) {
15         mex.push_back(sg(x * i));
16     }
17     sort(mex.begin(), mex.end());
18     int len = mex.size();
19     for (int i = 0; i < len; i++) {
20         if (i != mex[i]) return mp[x] = i;
21     }
22     return mp[x] = mex[len-1] + 1;
23 }
24 
25 int main() {
26     while (cin >> n) {
27         mp.clear();
28         if (sg(1ll)) cout << "Stan wins." << endl;
29         else cout << "Ollie wins." << endl;
30     }
31     return 0;
32 }
View Code

HDU 1847 -- Good Luck in CET-4 Everybody! (簡單求sg):

 1 #include <iostream>
 2 #include <cstring>
 3 #define MAXN 1010
 4 #define MAXM 11
 5 using namespace std;
 6 int sg[MAXN], f[MAXM];
 7 bool Hash[MAXN];
 8 
 9 void getSG(int m) {
10     memset(sg, 0, sizeof(sg));
11     for (int i = 1; i < MAXN; i++) {
12         memset(Hash, false, sizeof(Hash));
13         for (int j = 0; j < m && f[j] <= i; j++)
14             Hash[sg[i-f[j]]] = true;
15         for (int j = 0; j < MAXN; j++) {
16             if (!Hash[j]) {
17                 sg[i] = j;
18                 break;
19             }
20         }
21     }
22 }
23 
24 int main() {
25     int n, num = 1;
26     for (int i = 0; i < MAXM; num <<= 1, i++) f[i] = num;
27     getSG(MAXM);
28     while (cin >> n) {
29         if (sg[n]) cout << "Kiki" << endl;
30         else cout << "Cici" << endl;
31     }
32     return 0;
33 }
View Code

HDU 1848 -- Fibonacci again and again (分為三個子游戲,求原游戲sg值):

 1 #include <iostream>
 2 #include <cstring>
 3 #define MAXN 1010
 4 #define MAXM 100
 5 using namespace std;
 6 int sg[MAXN], f[MAXM];
 7 bool Hash[MAXN];
 8 
 9 int getFib() {
10     int i;
11     f[0] = 1, f[1] = 2;
12     for (i = 2; f[i] <= MAXN; i++) f[i] = f[i-1] + f[i-2];
13     return i;
14 }
15 void getSG(int m) {
16     memset(sg, 0, sizeof(sg));
17     for (int i = 1; i < MAXN; i++) {
18         memset(Hash, false, sizeof(Hash));
19         for (int j = 0; j < m && f[j] <= i; j++)
20             Hash[sg[i-f[j]]] = true;
21         for (int j = 0; j < MAXN; j++) {
22             if (!Hash[j]) {
23                 sg[i] = j;
24                 break;
25             }
26         }
27     }
28 }
29 
30 int main() {
31     int a, b, c;
32     getSG(getFib());
33     while (cin >> a >> b >> c && (a || b || c)) {
34         if (sg[a] ^ sg[b] ^ sg[c]) cout << "Fibo" << endl;
35         else cout << "Nacci" << endl;
36     }
37     return 0;
38 }
View Code

HDU 1079 -- Calendar Game (細致模擬):

 1 #include <iostream>
 2 #include <cstring>
 3 using namespace std;
 4 int sg[110][15][40];
 5 int day[110];
 6 int mm[15] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 7 
 8 int get_sg(int y, int m, int d) {
 9     if (sg[y][m][d] != -1) return sg[y][m][d];
10     if (m < 12 && d == mm[m] && d > mm[m+1]) {
11         if (get_sg(y, m + 1, 1)) return sg[y][m][d] = 0;
12         else return sg[y][m][d] = 1;
13     }
14     int a, b;
15     if (m == 2 && d == day[y] || m < 12 && d == mm[m] && d <= mm[m+1]) {
16         a = get_sg(y, m + 1, d), b = get_sg(y, m + 1, 1);
17     }
18     else if (m == 2 && d < day[y] || m < 12 && d < mm[m]) {
19         a = get_sg(y, m, d + 1), b = get_sg(y, m + 1, d);
20     }
21     else if (m == 12 && d < mm[12]) {
22         a = get_sg(y + 1, 1, d), b = get_sg(y, m, d + 1);
23     }
24     else if (m == 12 && d == mm[12]) {
25         a = get_sg(y + 1, 1, d), b = (y + 1, 1, 1);
26     }
27     if (a == 0 || b == 0) return sg[y][m][d] = 1;
28     else return sg[y][m][d] = 0;
29 }
30 
31 int main() {
32     int y, m, d, t;
33     memset(sg, -1, sizeof(sg));
34     for (int i = 1900; i <= 2001; i++) {
35         if (i % 4 == 0 && i % 100 || i % 400 == 0) day[i-1900] = 29;
36         else day[i-1900] = 28;
37     }
38     sg[101][11][4] = 0;
39     for (int i = 5; i <= mm[11]; i++) sg[101][11][i] = 1;
40     for (int i = 1; i <= mm[12]; i++) sg[101][12][i] = 1;
41     cin >> t;
42     while (t--) {
43         cin >> y >> m >> d;
44         y -= 1900;
45         if (get_sg(y, m, d)) cout << "YES" << endl;
46         else cout << "NO" << endl;
47     }
48     return 0;
49 }
View Code

HDU 1404 -- Digital Deletions (暴力打sg表):

 1 #include <stdio.h>
 2 #include <cstring>
 3 #define N 1000000
 4 using namespace std;
 5 bool sg[N];
 6 
 7 int get_len(int n) {
 8     int k = 1;
 9     while (n /= 10) k++;
10     return k;
11 }
12 void get_sg(int n) {
13     int len = get_len(n);
14     int base = 1;
15     for (int i = 0; i < len; i++) { // 將某一位變大都為必勝態
16         int m = n;
17         int tem = (m % (10 * base)) / base;
18         m -= tem * base;
19         for (int j = tem + 1; j <= 9; j++) sg[m+j*base] = true;
20         base *= 10;
21     }
22     base = 1;
23     while (len++ < 6) { // n后面補0...都為必勝態
24         n *= 10;
25         for (int i = 0; i < base; i++) sg[n+i] = true;
26         base *= 10;
27     }
28 }
29 
30 int main() {
31     memset(sg, false, sizeof(sg));
32 // 1為必敗態,能一步到達必敗態的都為必勝態,不能一步到達必敗態的都為必敗態
33     for (int i = 1; i < N; i++) if (!sg[i]) get_sg(i);
34     char s[10];
35     while (gets(s)) {
36         if (s[0] == '0') puts("Yes");
37         else {
38             int n = 0;
39             for (int i = 0; s[i]; i++) n = n * 10 + s[i] - '0';
40             if (sg[n]) puts("Yes");
41             else puts("No");
42         }
43     }
44     return 0;
45 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM