抄来的题解:
http://blog.youkuaiyun.com/jasison/article/details/27372913
1. LightOJ 1199 - Partitioning Game
题意:有n堆石子(1<=n<=100),每一堆分别有xi个石子(1<=xi<=10000),
一次操作可以使一堆石子变成两堆数目不相等的石子,
最后不能操作的算输,问先手胜还是后手胜。
思路:n堆石子相互独立,所以可以应用SG定理,只需要算出一堆石子的SG函数。
一堆石子(假设有x个)的后继状态可以枚举出来,分别是{1,x-1},{2,x-2},...,{(x-1)/2,x-(x-1)/2},
一堆石子分成的两堆石子又相互独立,再次应用SG定理。所以SG(x) = mex{ SG(1)^SG(x-1), SG(2)^SG(x-2),..., SG((x-1)/2)^SG(x-(x-1)/2) },
最后的答案是SG(x1)^SG(x2)^...^SG(xn)
附上代码(递归和非递归)及其效率比较:
#include <stdio.h>
int sg[11111];
int dfs(int n)
{
if(sg[n]!=-1)
return sg[n];
int res=0,i;
int vis[200];//vis不能初始化为全局变量,因为每一层dfs都需要一个‘自己’的vis数组
for(i=0;i<200;++i)
vis[i]=0;
for(i=1;i*2<n;++i)
vis[dfs(i)^dfs(n-i)] = 1;
i=0;
while(vis[i])
i++;
sg[n]=i;
return sg[n];
}
int main()
{
int T,n,cas=0,i,x,sum;
for(i=0;i<10001;++i)
sg[i]=-1;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
sum=0;
for(i=0;i<n;++i)
{
scanf("%d",&x);
sum^=dfs(x);
}
printf("Case %d: ",++cas);
if(sum)
puts("Alice");
else
puts("Bob");
}
return 0;
}
#include <stdio.h>
#define N 11111
int sg[N];
int u[N];
int dfs(int n)
{
int i,j;
sg[1]=0;
sg[2]=0;
for(i=3;i<n;++i)
{
for(j=0;j<n;++j)
u[j]=0;
for(j=1;j*2<i;++j)
{
u[sg[j]^sg[i-j]]=1;
}
int k=0;
while(u[k])
k++;
sg[i]=k;
}
}
int main()
{
dfs(10001);
int T,n,cas=0,i,x,sum;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
sum=0;
for(i=0;i<n;++i)
{
scanf("%d",&x);
sum^=sg[x];
}
printf("Case %d: ",++cas);
if(sum)
puts("Alice");
else
puts("Bob");
}
return 0;
}