zoukankan      html  css  js  c++  java
  • SG函数模板

    这篇虽然是转载的,但代码和原文还是有出入,我认为我的代码更好些。

    转载自:http://www.cnblogs.com/frog112111/p/3199780.html

    最新sg模板:

    //MAXN为所有堆最多石子的数量
    //f[]用来保存只能拿多少个 从0开始到num-1种情况 并且这里的f[]不需要排序
    const int MAXN = 1000 + 5 ;
    int f[MAXN],sg[MAXN];
    bool vis[MAXN];
    void sgsol(int num,int N)
    {
        int i,j;
        memset(sg,0,sizeof(sg));
        for(i=1;i<=N;i++)
        {
            memset(vis,0,sizeof(vis));
            for(j=0;j<num;j++)
            {
                if (i-f[j]>=0)
                    vis[sg[i-f[j]]]=1;
            }
            for(j=0;;j++)
                if(!vis[j])break;
            sg[i]=j;
        }
    }

    首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

    对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数g如下:g(x)=mex{ g(y) | y是x的后继 },这里的g(x)即sg[x]

    例如:取石子问题,有1堆n个的石子,每次只能取{1,3,4}个石子,先取完石子者胜利,那么各个数的SG值为多少?

    sg[0]=0,f[]={1,3,4},

    x=1时,可以取走1-f{1}个石子,剩余{0}个,mex{sg[0]}={0},故sg[1]=1;

    x=2时,可以取走2-f{1}个石子,剩余{1}个,mex{sg[1]}={1},故sg[2]=0;

    x=3时,可以取走3-f{1,3}个石子,剩余{2,0}个,mex{sg[2],sg[0]}={0,0},故sg[3]=1;

    x=4时,可以取走4-f{1,3,4}个石子,剩余{3,1,0}个,mex{sg[3],sg[1],sg[0]}={1,1,0},故sg[4]=2;

    x=5时,可以取走5-f{1,3,4}个石子,剩余{4,2,1}个,mex{sg[4],sg[2],sg[1]}={2,0,1},故sg[5]=3;

    以此类推.....

       x         0  1  2  3  4  5  6  7  8....

    sg[x]      0  1  0  1  2  3  2  0  1....

    计算从1-n范围内的SG值。

    f(存储可以走的步数,f[0]表示可以有多少种走法)

    f[]需要从小到大排序

    1.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);

    2.可选步数为任意步,SG(x) = x;

    3.可选步数为一系列不连续的数,用GetSG()计算

    SG 打表 模板:

    //求[1,n]的sg函数值
    //f[0]:可以取的方案数 f[1]~f[n]每个方案可以取的石子数
    //sg[]:0~n的SG函数值 Hash[]:为了求最小非负整数
    const int N = 1000 + 5 ;
    int f[N], sg[N], Hash[N];
    void SGsol(int n)
    {
        int i, j;
        memset(sg, 0, sizeof(sg));
        for(i = 1; i <= n; i++)
        {
            memset(Hash, 0, sizeof(Hash));
            //这的j要小于f[0],因为只有f[0]种情况
            for(j = 1; f[j] <= i && j <= f[0]; j++)
                Hash[sg[i - f[j]]] = 1;
            //求mes{}中未出现的最小的非负整数
            for(j = 0;; j++)          
            {
                if(Hash[j] == 0)
                {
                    sg[i] = j;
                    break;
                }
            }
        }
    }

    hdu  1848

    题意:

    取石子问题,一共有3堆石子,每次只能取斐波那契数个石子,先取完石子者胜利,问先手胜还是后手胜

    题解:

    先把斐波那契存到f[]里,求1000以内的sg值,之后3个异或就好了。注意 异或的时候一定要加括号,因为^的优先级比==小,也就是说,不加括号,会先算==,之后再异或

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 1000 + 5 ;
    int f[N], sg[N], Hash[N], cnt;
    void SGsol(int n)
    {
        int i, j;
        memset(sg, 0, sizeof(sg));
        for(i = 1; i <= n; i++)
        {
            memset(Hash, 0, sizeof(Hash));
            for(j = 0; f[j] <= i && j < cnt; j++)
                Hash[sg[i - f[j]]] = 1;
            for(j = 0;; j++)
            {
                if(Hash[j] == 0)
                {
                    sg[i] = j;
                    break;
                }
            }
        }
    }
    
    void Solve()
    {
        f[1]=f[0] = 1, f[2] = 2, cnt = 3;
        for(int i = 3;; i++)
        {
            f[cnt++] = f[i - 1] + f[i - 2];
            if(f[i] > 1000)
            {
                cnt--;
                break;
            }
        }
        SGsol(N);
        int a, b, c;
        while(cin >> a >> b >> c)
        {
            if(a + b + c == 0) break;
            if((sg[a]^sg[b]^sg[c]) == 0) puts("Nacci");
            else puts("Fibo");
        }
    }
    
    int main()
    {
        Solve();
        return 0;
    }
  • 相关阅读:
    CF Round433 B. Jury Meeting
    CF Round433 C. Planning
    繁忙的都市
    联络员
    组合数模板
    Rinne Loves Xor
    [SDOI2016]齿轮
    水题(water)
    Music Problem
    小H和游戏
  • 原文地址:https://www.cnblogs.com/s1124yy/p/5750902.html
Copyright © 2011-2022 走看看