Description
有多堆石子,每次只能从一堆石子取走集合S中某一元素数值的石子,两人轮流取,谁不能取谁输,假设两人足够机智,问先手输赢
Input
多组大用例,每组大用例第一行首先输入集合S的元素数k,之后输入k个整数表示S中的元素,第二行输入小用例组数m,之后m行每行首先输入石子堆数n,之后n个数表示每堆石子的数量,以k=0结束输入
Output
每组大用例输出占一行,对于每组小用例,如果先手赢则输出W,否则输出L
Sample Input
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
Sample Output
LWW
WWL
Solution
经典S-Nim游戏,这类博弈游戏解题关键在于如何求出sg值,对于每堆石子,其sg值取其所有后继状态sg中没有出现过的最小自然数,求出所有堆石子的sg值之后取异或和判断是否为0,为0说明先手输,否则先手赢
Code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 11111
int k,n,m,s[111],sg[maxn];
int dfs(int x)//求石子数量为x的石子堆的sg值
{
if(sg[x]!=-1)//
return sg[x];
bool zrs[111];
memset(zrs,false,sizeof(zrs));//初始化自然数集合
for(int i=0;i<k;i++)
if(x>=s[i])//表示所有后继状态,将sg值标记
{
dfs(x-s[i]);
zrs[sg[x-s[i]]]=true;
}
for(int i=0;;i++)//所有后继状态中没有出现过的最小自然数即为其sg值
if(!zrs[i])
return sg[x]=i;
}
int main()
{
while(scanf("%d",&k),k)
{
memset(sg,-1,sizeof(sg));//初始化
for(int i=0;i<k;i++)
scanf("%d",&s[i]);
sort(s,s+k);//对集合中元素排序
scanf("%d",&n);
while(n--)
{
scanf("%d",&m);
int temp,ans=0;
while(m--)
{
scanf("%d",&temp);
ans^=dfs(temp);//对每堆石子的sg值取异或
}
if(ans) printf("W");
else printf("L");
}
printf("\n");
}
return 0;
}