题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1536
题意:
输入:
2 2 5
3
2 5 12
3 2 4 7
4 2 3 7 12
5 1 2 3 4 5
3
2 5 12
3 2 4 7
4 2 3 7 12
0
输出:
LWW
WWL
第一行给出可行操作集合大小为k,然后k个数表示一次能在一堆里去这些数量的石子。先拿完者赢。
第二行一个数m表示将会进行m次游戏。
下面m行,每行第一个数n表示石子的堆数,后面n堆石子的数量。每次游戏先手赢输出W,否则输出L。每个样例所有游戏次数输出在一行。
求SG函数,可以用下面两个模板。
1、递归
//N为一堆里最大石子数量
//M为可操作集合的最大数量
//x为实际一堆里多少石子
//k为实际可操作集合s为多大
//注意:sg需要初始化为-1,每个集合初始化一遍即可,s需要初始化由小到大排序
int s[N], sg[N], k;
int getsg(int x) {
int i;
if(sg[x]!=-1) return sg[x];
bool vis[M];
memset(vis,0,sizeof(vis));
for(i=0; i < k && x>=s[i]; i++) vis[getsg(x-s[i])]=1;
for(i=0; ; i++) {
if(!vis[i]) {
sg[x]=i;
break;
}
}
return sg[x];
}
2、打表
//N为一堆里最大石子数量
//M为可操作集合的最大数量
//n表示要打多大的表
//k为实际可操作集合s为多大
//注意:s需要初始化由小到大排序
bool mex[M];
int sg[N], s[M], k;
void getsg(int n) {
memset(sg, 0, sizeof sg);
int i, j;
for(i = 0; i <= n; i++) {
memset(mex, 0, sizeof mex);
for(j = 0; j < k && i >= s[j]; j++) {
mex[sg[i - s[j]]] = 1;
}
for(j = 0; ; j++) {
if(mex[j] == 0) {
sg[i] = j;
break;
}
}
}
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
bool mex[10010];
int sg[10010], s[110], k;
void getsg() {
memset(sg, 0, sizeof sg);
int i, j;
for(i = 0; i < 10001; i++) {
memset(mex, 0, sizeof mex);
for(j = 0; j < k && i >= s[j]; j++) {
mex[sg[i - s[j]]] = 1;
}
for(j = 0; ; j++) {
if(mex[j] == 0) {
sg[i] = j;
break;
}
}
}
}
int main() {
while(~scanf("%d", &k) , k) {
int i, j;
for(i = 0; i < k; i++) {
scanf("%d", s + i);
}
sort(s, s + k);
getsg();
int m;
scanf("%d", &m);
while(m--) {
int n;
scanf("%d", &n);
int ans = 0, tmp;
for(i = 0; i < n; i++) {
scanf("%d", &tmp);
ans ^= sg[tmp];
}
if(ans) putchar('W');
else putchar('L');
}
putchar('\n');
}
return 0;
}