HDU 1536 S-Nim(博弈论)

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;
}

你可能感兴趣的:(HDU 1536 S-Nim(博弈论))