昨天我们省的大神犇xys大佬,要跟我玩一个小游戏,大概是有几个数,每次你可以选择一个,减去某个值,最后到谁这些数都被减成0,谁就输。很不巧,我一眼就看穿了这是一个先手必胜的游戏,然后我选了先手,然后……我记错了必胜策略,然后就在大佬的微笑中输掉了比赛……
据说这个SG函数的证明过程比较复杂,我就大概会先写一下,他是啥,怎么做,就不去证明了。
我们定义必输的局面的SG函数值为0,我们定义,SG[i]是i没出现在i所能走到的所有状态中,最小的自然数。
而对于局面 i j …… k 我们只要吧SG[i] SG[j] SG[k] xor 到一起,如果最后的值为0,则为必败局面,否则为必胜局面。
这样子 我们考虑如何求出SG函数,我们观察发现,i所能到达的状态显然是i-某个值,所以我们可以从头开始递推,并且在局部采取类似基数排序的方法求出SG函数
这样子我们给出一道稍有变形的例题 hdu1536
这道题只是对减去的值做了限制,其他是一样的。
给出AC代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n,m,k,tp,ans;
int sz[11000],c[120],sg[11000];
void sg_init()
{
for (int i = 1;i <= 10000;i++)
{
memset(c,0,sizeof(c));
for (int j = 1;j <= n;j++)
{
if(i - sz[j] >= 0) c[sg[i - sz[j]]] = 1;
}
for (int j = 0;j <= 100;j++) if (!c[j]) {sg[i] = j;break;}
}
}
int main()
{
while(1)
{
scanf("%d",&n);
if (!n) return 0;
for (int i = 1;i <= n;i++) scanf("%d",&sz[i]);
sg_init();
scanf("%d",&m);
for (int i = 1;i <= m;i++)
{
ans = 0;
scanf("%d",&k);
for (int i = 1;i <= k;i++)
{
scanf("%d",&tp);
ans ^= sg[tp];
}
if (ans) printf("W");else printf("L");
}
printf("\n");
}
}