POJ2960 寻找SG值

来源:互联网 发布:php简单博客源码 编辑:程序博客网 时间:2024/06/08 09:51

题目:题目链接


这道题目的意思是说:大意:有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那么必输


就是每次都对一堆石子求SG,依次尝试集合中的数字能否被取到,取到的再求SG


#include <iostream>#include <cstdio>#include <string>#include <string.h>#include <map>#include <vector>#include <cstdlib>#include <cmath>#include <algorithm>#include <queue>#include <set>#include <stack>using namespace std;int sk[200];int sg[10010];int t;int find(int n){    if(sg[n]!=-1)        return sg[n];    int vis[110]= {0};    for(int i=0; sk[i]<=n&&i<t; i++)    {        if(sg[n-sk[i]]==-1)            sg[n-sk[i]]=find(n-sk[i]);        vis[sg[n-sk[i]]]=1;    }    for(int i=0;; i++)        if(vis[i]!=1)            return i;}int main(){    int n, m, k;    while(scanf("%d", &t))    {        if(!t)            break;        memset(sg, -1, sizeof(sg));        sg[0] = 0;        for(int i = 0; i < t; ++i)            scanf("%d", &sk[i]);        sort(sk, sk+t);        scanf("%d", &n);        while(n--)        {            int sum = 0;            scanf("%d", &m);            for(int i = 0; i < m; ++i)            {                scanf("%d",&k);                sum ^= find(k);            }            if(sum)                printf("W");            else printf("L");        }        printf("\n");    }    return 0;}

努力努力...