先,定义一下 状态Position P 先手必败 N x先手必胜

操作方法: 反向转移

相同状态 不同位置 的一对 相当于无

对于ICG游戏,我们可以将游戏中每一个可能发生的局面表示为一个点。并且若存在局面i和局面j,且j是i的后继局面(即局面i可以转化为局面j),我们用一条有向边,从i出发到j,连接表示局面i和局面j的点。则整个游戏可以表示成为一个有向无环图:

根据ICG游戏的定义我们知道,任意一个无法继续进行下去的局面为终结局面,即P局面(先手必败)。在上图中我们可以标记所有出度为0的点为P点。接着根据ICG游戏的两条性质,我们可以逆推出所有点为P局面还是N局面:

对于一个游戏可能发生的局面x,我们如下定义它的sg值:
(1)若当前局面x为终结局面,则sg值为0。
(2)若当前局面x非终结局面,其sg值为:sg(x) = mex{sg(y) | y是x的后继局面}。
mex{a[i]}表示a中未出现的最小非负整数。举个例子来说:
mex{0, 1, 2} = 3, mex{1, 2}=0, mex{0,1,3}=2

我们将上图用sg函数表示后,得到:

可以发现,若一个局面x为P局面,则有sg(x)=0;否则sg(x)>0。同样sg值也满足N、P之间的转换关系:
若一个局面x,其sg(x)>0,则一定存在一个后续局面y,sg(y)=0。
若一个局面x,其sg(x)=0,则x的所有后续局面y,sg(y)>0。

由上面的推论,我们可以知道用N、P-Position可以描述的游戏用sg同样可以描述。并且在sg函数中还有一个非常好用的定理,叫做sg定理:
对于多个单一游戏,X=x[1..n],每一次我们只能改变其中一个单一游戏的局面。则其总局面的sg值等于这些单一游戏的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()计算

//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//hash[]:mex{}
int f[N],sg[N],hash[N];
void getSG(int n)
{
int i,j;
memset(sg,,sizeof(sg));
for(i=;i<=n;i++)
{
memset(hash,,sizeof(hash));
for(j=;f[j]<=i;j++)
hash[sg[i-f[j]]]=;
for(j=;j<=n;j++) //求mes{}中未出现的最小的非负整数
{
if(hash[j]==)
{
sg[i]=j;
break;
}
}
}
}

SG打表

//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[],sg[],n;
int SG_dfs(int x)
{
int i;
if(sg[x]!=-)
return sg[x];
bool vis[];
memset(vis,,sizeof(vis));
for(i=;i<n;i++)
{
if(x>=s[i])
{
SG_dfs(x-s[i]);
vis[sg[x-s[i]]]=;
}
}
int e;
for(i=;;i++)
if(!vis[i])
{
e=i;
break;
}
return sg[x]=e;
}

dfs

注意在SG表的初始化中,不用每次都初始;否则会T的,因为可以循环利用,这是一个强大的地方

HDU1536 实战

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int s[],sg[],n;
char op[];
int SG_dfs(int x)
{
int i;
if(sg[x]!=-)
return sg[x];
bool vis[];
memset(vis,,sizeof(vis));
for(i=;i<n;i++)
{
if(x>=s[i])
{
SG_dfs(x-s[i]);
vis[sg[x-s[i]]]=;
}
}
int e;
for(i=;;i++)
if(!vis[i])
{
e=i;
break;
}
return sg[x]=e;
}
int main()
{
int k;
while(scanf("%d",&n)!=EOF)
{
if(n==)
break;
for(int i= ; i<n ; i++)
scanf("%d",&s[i]);
sort(s,s+n);
int m,cnt=;
scanf("%d",&m);
memset(sg,-,sizeof(sg));
for(int i= ; i<m ; i++)
{ scanf("%d",&k);
int x=;
while(k--)
{
int w;
scanf("%d",&w);
x^=SG_dfs(w); }
if(x!=)
printf("W");
else
printf("L");
}
puts("");
} return ;
}

什么是SG?+SG模板的更多相关文章

  1. SG函数模板(转)

    ps:sg[i]为0表示i节点先手必败. 首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数.例如mex{0,1,2,4}=3.me ...

  2. hdu 1536 SG函数模板题

    S-Nim Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  3. HDU 1847-Good Luck in CET-4 Everybody!-博弈SG函数模板

    Problem Description 大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此.当然,作为在考场浸润了十几载 ...

  4. hdu1536&&hdu3023 SG函数模板及其运用

    S-Nim Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status ...

  5. SG函数模板

    这篇虽然是转载的,但代码和原文还是有出入,我认为我的代码更好些. 转载自:http://www.cnblogs.com/frog112111/p/3199780.html 最新sg模板: //MAXN ...

  6. hdu 1536 S-Nim(sg函数模板)

    转载自:http://blog.csdn.net/sr_19930829/article/details/23446173 解题思路: 这个题折腾了两三天,参考了两个模板,在这之间折腾过来折腾过去,终 ...

  7. 【非原创】sg函数模板

    学习博客:戳这里 解题模型: 1.把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或.        即sg(G)=sg(G1)^sg(G2)^...^sg(Gn) ...

  8. SG函数 模板

    int get_SG(int x) { ) return SG[x]; ]={}; ;i<=n;i++) ) v[get_SG(x-s[i])]=; int i; ;v[i];i++); SG[ ...

  9. Light OJ 1199:Partitioning Game(SG函数模板)

    Alice and Bob are playing a strange game. The rules of the game are: 1.      Initially there are n p ...

  10. SG函数模板(洛谷2197nim游戏

    #include <iostream> #include <cstdio> #include <queue> #include <algorithm> ...

随机推荐

  1. POJ2774Long Long Message (后缀数组&后缀自动机)

    问题: The little cat is majoring in physics in the capital of Byterland. A piece of sad news comes to ...

  2. NYOJ-求逆序数 ----------------待解决,WA

    描述 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数. 现在,给你一个N个元素的序列,请你判断出它的逆序数 ...

  3. Poj1218_THE DRUNK JAILER(水题)

    一.Description A certain prison contains a long hall of n cells, each right next to each other. Each ...

  4. 【转】Pro Android学习笔记(十六):用户界面和控制(4):ImageView控件

    目录(?)[-] XML片段 代码设置ImageView ImageView是基础的控件,它是android.widget.ImageView的继承类. XML片段      <LinearLa ...

  5. wpf 可视化树的注意点

    将控件的visibility 设置为collapse时,控件的可视化树并未生成,此时并不能查找到控件内的子控件. 若某控件想对用户不可见,且可以生成可视化树,则需将控件的visibility 设置为h ...

  6. nmp部署(Nginx Mariadb Php-fpm)

    #主机:192.168.2.129(mini2) 既是php主机,也是数据库主机#yum install -y php php-fpm php-mysql mariadb-server[root@~ ...

  7. 【转】GitHub使用

    1.设置Git全局用户配置 git config --global user.name "xxx" git config --global user.email xxx@gmail ...

  8. badblocks 检查磁盘损坏的区块

    Linux badblocks命令用于检查磁盘装置中损坏的区块. 语法: badblocks [-svw][-b <区块大小>][-o <输出文件>][磁盘装置][磁盘区块数] ...

  9. [51nod1138]正整数分解为几个连续自然数之和

    解题关键:注意为什么上界是$\sqrt {2n} $ 因为函数是关于m的递减函数,而结果必须为正整数 $a = \frac{{2n + m - {m^2}}}{{2m}} = \frac{n}{m} ...

  10. idea中java项目增加module后,增加的目录(src)无法增加包(Package)

    在idea项目中,增肌model后,在项目根目录下增加src目录,右键发现无法增加包(Package). 仔细观察发现,新增加的src目录是棕色,而原先的src目录是浅蓝色的,见下图: 在src右键, ...