1. 程式人生 > 實用技巧 >HDU-3032--Nim or not Nim?(博弈+SG打表)

HDU-3032--Nim or not Nim?(博弈+SG打表)

題目分析:

這是一個經典的Multi-SG遊戲的問題。

相較於普通的Nim遊戲,該遊戲僅僅是多了拆成兩堆這樣的一個狀態。即多了一個SG(x+y)的過程。

而根據SG定理,SG(x+y)這個遊戲的結果可以拆成SG(x)和 SG(y)遊戲的結果的xor。

因此,在我們求SG函式的過程中,我們只需要再列舉一下拆成兩堆的狀態,即可獲得Multi-SG遊戲每個狀態的SG函式。

但是題目中的資料範圍較大(達到了1e5的級別),因此我們考慮先打表找規律。打出SG函式後不難發現,存在規律:

順著上面的規律即可求出每一個狀態的SG函式,最後將這些SG函式結果全都xor起來即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 100;
typedef long long ll;
ll n, a[N];
string Answer[2] = { "Alice\n","Bob\n" };

ll gsg(int x) {
	if (x % 4 == 0)return x - 1;
	else if (x % 4 == 3)return x + 1;
	else return x;
}

int main() {
	//freopen("in.txt", "r", stdin);
	ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int t; cin >> t;
	while (t--) {
		ll cnt = 0;
		cin >> n;
		for (int i = 1; i <= n; ++i) {
			cin >> a[i];
			cnt ^= gsg(a[i]);
		}
		if (cnt == 0)cout << Answer[1];
		else cout << Answer[0];
	}
}

SG打表程式碼:

//SG打表的程式碼
const int maxn = 105;
int vis[maxn];
int sg[maxn];
void init() {//SG函式打表
	for (int i = 0; i < 100; i++) {//列舉100個狀態
		memset(vis, 0, sizeof(vis));
		for (int j = 1; j <= i; j++) {
			vis[sg[i - j]] = true;
		}
		for (int j = 1; j < i; j++) {//列舉分成兩塊的狀態
			vis[sg[j] ^ sg[i - j]] = true;
		}
		for (int j = 0;; j++) {
			if (!vis[j]) {
				sg[i] = j;
				break;
			}
		}
	}
	for (int i = 0; i < 100; i++) {
		cout << i << " " << sg[i] << endl;
	}
}