HDU 1536 S-Nim (SG定理)

来源:互联网 发布:知乎不能回答问题 编辑:程序博客网 时间:2024/06/05 04:35

题目:

有n堆珠子,各堆的珠子数是确定的(不需相同),两人轮流从其中一堆中拿走一些珠子,最后没有珠子可拿的人输了。

这题与Nim游戏不同的是每次拿走的珠子数只能是 给定集合中的数。

分析:

这就是考察SG定理的直接应用,每一堆都可看成一个子游戏,游戏和第SG值就是"各子游戏的SG值的Nim和“ (即异或值)


代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>using namespace std;int a[110],f[10010],k;// a[] 存每次可取走的个数;   f[] 存各子游戏的SG函数值//计算子游戏的SG值,SG(x)=mex(S),其中S是x的后继状态的SG函数值//的集合,mex(S)表示不再S内的最小非负整数int mex(int u){    if(f[u]>=0) return f[u];    bool flag[101]={0};           //flag[]用于标记是否在集合S内,数组开101就够了是因为由mex函数的定义可知它的最大值等于后继状态的个数    for(int i=0;a[i]<=u&&i<k;i++){        int t=u-a[i];             //枚举后继状态        if(f[t]==-1) f[t]=mex(t); //计算后继状态的SG函数值,并记忆化,避免重复计算        flag[f[t]]=true;    }    for(int i=0;;i++){            //按定义求解mex函数值        if(!flag[i]) return i;    }}int main(){    //freopen("out.txt","w",stdout);    while(scanf("%d",&k),k)    {        memset(f,-1,sizeof(f));        for(int i=0;i<k;i++)            scanf("%d",&a[i]);        sort(a,a+k);                   //排序,方便枚举后继状态,剪去不必要的枚举        int m,n;        scanf("%d",&m);        while(m--){            int sg=0,x;            scanf("%d",&n);            for(int i=0;i<n;i++){                scanf("%d",&x);                if(f[x]==-1) f[x]=mex(x);                sg^=f[x];            //SG定理:游戏和的SG函数值等于个子游戏SG函数值的Nim和(Xor)            }            if(sg) printf("W");            else printf("L");        }        printf("\n");    }    return 0;}