您的位置:首页 > 其它

POJ 2960 S-Nim【SG函数的应用】

2011-03-27 20:48 423 查看
http://poj.org/problem?id=2960
POJ 2960 S-Nim
大意:有n堆石子,每堆石子个数已知,两人轮流从中取石子,
每次可取的石子数x满足x属于集合S(k) = {s1,s2,s3...sk-1},问先拿者是否有必胜策略?
分析:
1.可将问题转化为n个子问题,每个子问题分别为:
从一堆x颗石子中取石子,每次可取的石子数为集合S(k)中的一个数
2.分析(1)中的每个子问题,
易得:SG(x) = mex(SG[x-s[i]])(0<i<k-1);
3.后面就是SG函数的应用,根据Sprague-Grundy Therem:g(G)=g(G1)^g(G2)^g(G3)^...^g(Gn)
即游戏的和的SG函数值是它的所有子游戏的SG函数值的异或,即
SG(G) = SG(x1)^SG(x2)^...^SG(xn),故若SG(G)=0那么必输

View Code

#include<stdio.h>
#include<string.h>
const int N = 10001;
const int M= 101;
int SG
;//SG[i]记录一堆i颗石子的SG状态
int s[M];//存储可选取的石子数目集合
int k;//s[]集合中的元素个数

void DFS(int x)//递归求解SG
{
if(SG[x]!=-1)return;
bool visited
;
for(int i=0;i<=x;i++)
visited[i]=false;
for(int i=0;i<k;i++)
{
int temp=x-s[i];
if(temp>=0)
{
if(SG[temp]==-1)
DFS(temp);
visited[SG[temp]]=true;
}
}
for(int i=0;i<N;i++)
if(!visited[i])
{
SG[x]=i;
return;
}
}
int main()
{
while(scanf("%d",&k)!=EOF)//可选集合中的元素个数
{
if(k==0)break;
int i,ans;
for(i=0;i<k;i++)
scanf("%d",&s[i]);
memset(SG,-1,sizeof(SG));
SG[0]=0;
int m;
scanf("%d",&m);//测试组数
while(m--)
{
ans=0;
int l;
scanf("%d",&l);//石子堆数
while(l--)
{
int x;
scanf("%d",&x);//当前堆石子数目
if(SG[x]==-1)
DFS(x);
ans=ans^SG[x];
}

if(ans==0)printf("L");
else
printf("W");
}

printf("\n");
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: