給定 n 個合法取子數 `move[i]`。進行 m 個查詢,每查詢給出 l 堆石子,每堆大小為 `num`。兩人輪流取子,每次從任意一堆取走 `move[]` 中的某個數量,先無法操作者負。判斷先手是否必勝(W)或必敗(L)。
題目中石子數上限 10000,直接 DP 計算 `sg[0..10000]` 即可。觀察發現 SG 函數具有周期性(取決於 `move[]` 集合),大數據下可進一步優化,但本題直接預計算足夠。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 10005;
int n, moves[110];
int sg[MAXN];
void computeSG() {
bool vis[MAXN];
sg[0] = 0;
for (int i = 1; i < MAXN; i++) {
memset(vis, 0, sizeof(vis));
for (int j = 0; j < n; j++) {
if (i >= moves[j]) vis[sg[i - moves[j]]] = true;
}
for (int g = 0; ; g++) if (!vis[g]) { sg[i] = g; break; }
}
}
int main() {
while (scanf("%d", &n) == 1 && n) {
for (int i = 0; i < n; i++) scanf("%d", &moves[i]);
computeSG();
int m; scanf("%d", &m);
while (m--) {
int l; scanf("%d", &l);
int xorSum = 0;
while (l--) {
int x; scanf("%d", &x);
xorSum ^= sg[x];
}
putchar(xorSum ? 'W' : 'L');
}
putchar('\n');
}
return 0;
}
此題是 SG 函數入門經典題,掌握「單堆 SG 計算 + 多堆異或判斷」模板,可解決大部分取石子類組合博弈問題。