• SG函数模板


    首先定义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()计算

    模板1如下(SG打表):

     1 //f[]:可以取走的石子个数
     2 //sg[]:0~n的SG函数值
     3 //hash[]:mex{}
     4 int f[N],sg[N],hash[N];     
     5 void getSG(int n)
     6 {
     7     int i,j;
     8     memset(sg,0,sizeof(sg));
     9     for(i=1;i<=n;i++)
    10     {
    11         memset(hash,0,sizeof(hash));
    12         for(j=1;f[j]<=i;j++)
    13             hash[sg[i-f[j]]]=1;
    14         for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
    15         {
    16             if(hash[j]==0)
    17             {
    18                 sg[i]=j;
    19                 break;
    20             }
    21         }
    22     }
    23 }
    View Code

    模板2如下(dfs):

     1 //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
     2 //n是集合s的大小 S[i]是定义的特殊取法规则的数组
     3 int s[110],sg[10010],n;
     4 int SG_dfs(int x)
     5 {
     6     int i;
     7     if(sg[x]!=-1)
     8         return sg[x];
     9     bool vis[110];
    10     memset(vis,0,sizeof(vis));
    11     for(i=0;i<n;i++)
    12     {
    13         if(x>=s[i])
    14         {
    15             SG_dfs(x-s[i]);
    16             vis[sg[x-s[i]]]=1;
    17         }
    18     }
    19     int e;
    20     for(i=0;;i++)
    21         if(!vis[i])
    22         {
    23             e=i;
    24             break;
    25         }
    26     return sg[x]=e;
    27 }
    View Code

    hdu  1848

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

    1. 可选步数为一系列不连续的数,用GetSG(计算) 
    2. 最终结果是所有SG值异或的结果 

    AC代码如下:

     1 #include<stdio.h>
     2 #include<string.h>
     3 #define N 1001
     4 //f[]:可以取走的石子个数
     5 //sg[]:0~n的SG函数值
     6 //hash[]:mex{}
     7 int f[N],sg[N],hash[N];     
     8 void getSG(int n)
     9 {
    10     int i,j;
    11     memset(sg,0,sizeof(sg));
    12     for(i=1;i<=n;i++)
    13     {
    14         memset(hash,0,sizeof(hash));
    15         for(j=1;f[j]<=i;j++)
    16             hash[sg[i-f[j]]]=1;
    17         for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
    18         {
    19             if(hash[j]==0)
    20             {
    21                 sg[i]=j;
    22                 break;
    23             }
    24         }
    25     }
    26 }
    27 int main()
    28 {
    29     int i,m,n,p;
    30     f[0]=f[1]=1;
    31     for(i=2;i<=16;i++)
    32         f[i]=f[i-1]+f[i-2];
    33     getSG(1000);
    34     while(scanf("%d%d%d",&m,&n,&p)!=EOF)
    35     {
    36         if(m==0&&n==0&&p==0)
    37             break;
    38         if((sg[m]^sg[n]^sg[p])==0)
    39             printf("Nacci
    ");
    40         else
    41             printf("Fibo
    ");
    42     }
    43     return 0;
    44 }
    View Code

    hdu  1536

    题意:首先输入K 表示一个集合的大小  之后输入集合 表示对于这对石子只能去这个集合中的元素的个数

    之后输入 一个m 表示接下来对于这个集合要进行m次询问 

    之后m行 每行输入一个n 表示有n个堆  每堆有n1个石子  问这一行所表示的状态是赢还是输 如果赢输入W否则L

    思路:对于n堆石子 可以分成n个游戏 之后把n个游戏合起来就好了
     
    AC代码如下:
     1 #include<stdio.h>
     2 #include<string.h>
     3 #include<algorithm>
     4 using namespace std;
     5 //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
     6 //n是集合s的大小 S[i]是定义的特殊取法规则的数组
     7 int s[110],sg[10010],n;
     8 int SG_dfs(int x)
     9 {
    10     int i;
    11     if(sg[x]!=-1)
    12         return sg[x];
    13     bool vis[110];
    14     memset(vis,0,sizeof(vis));
    15     for(i=0;i<n;i++)
    16     {
    17         if(x>=s[i])
    18         {
    19             SG_dfs(x-s[i]);
    20             vis[sg[x-s[i]]]=1;
    21         }
    22     }
    23     int e;
    24     for(i=0;;i++)
    25         if(!vis[i])
    26         {
    27             e=i;
    28             break;
    29         }
    30     return sg[x]=e;
    31 }
    32 int main()
    33 {
    34     int i,m,t,num;
    35     while(scanf("%d",&n)&&n)
    36     {
    37         for(i=0;i<n;i++)
    38             scanf("%d",&s[i]);
    39         memset(sg,-1,sizeof(sg));
    40         sort(s,s+n);
    41         scanf("%d",&m);
    42         while(m--)
    43         {
    44             scanf("%d",&t);
    45             int ans=0;
    46             while(t--)
    47             {
    48                 scanf("%d",&num);
    49                 ans^=SG_dfs(num);
    50             }
    51             if(ans==0)
    52                 printf("L");
    53             else
    54                 printf("W");
    55         }
    56         printf("
    ");
    57     }
    58     return 0;
    59 }
    View Code
  • 相关阅读:
    Linux负载均衡--LVS(IPVS)主要算法实现分析
    使用alarm控制阻塞connect()超时的示例
    使用select控制非阻塞connect()超时的示例
    再出发
    nulls_hlist原理 和 tcp连接查找
    linux支持大容量硬盘
    Nmap扫描原理(下)
    linux常用命令
    Linux下面自动清理超过指定大小的文件
    Memcached介绍
  • 原文地址:https://www.cnblogs.com/frog112111/p/3199780.html
Copyright © 2020-2023  润新知