SG函数

sg[i]为0表示i节点先手必败。

首先定义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}个石子,剩余{0}个,mex{sg[0]}=mex{0},故sg[1]=1;

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

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

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

x=5时,可以取走{1,3,4}个石子,剩余{4,2,1}个,mex{sg[4],sg[2],sg[1]}=mex{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. 打表

//f[]: 可以取走的石子数量
//sg[]: 1~n的sg函数值
//vis[]: mex{}
void getSG(int n) {
memset(sg, , sizeof(sg));
for (int i = ; i <= n; i++) {
memset(vis, , sizeof(vis));
for (int j = ; f[j] <= i && j < maxm; j++)
vis[sg[i - f[j]]] = ;
for (int j = ;; j++) if (!vis[j]) { //最小的未出现的正整数
sg[i] = j;
break;
}
}
}

2. 记忆化搜索

//记忆化搜索
//f[]: 从小到大排序
//sg[]: 初始化为-1
//maxm,石子个数,集合的最大数量
int dp(int x)
{
if (sg[x] != -) return sg[x];
bool vis[maxn];
memset(vis, , sizeof(vis));
for (int i = ; i < maxm; i++)
{
if (f[i] <= x)
{
dp(x - f[i]);
vis[sg[x - f[i]]] = ;
}
}
for (int i = ;; i++)
{
if (!vis[i]) return sg[x] = i;
}
}

HDU 1848

今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下:
1、  这是一个二人游戏;

2、  一共有3堆石子,数量分别是m, n, p个;

3、  两人轮流走;

4、  每走一步可以选择任意一堆石子,然后取走f个;

5、  f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);

6、  最先取光所有石子的人为胜者;

假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。

代码:

 #include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std; const int maxn = + ;
const int maxm = ; //石子个数 int f[maxm], sg[maxn];
bool vis[maxn];
//f[]: 可以取走的石子数量
//sg[]: 1~n的sg函数值
//vis[]: mex{}
void getSG(int n) {
memset(sg, , sizeof(sg));
for (int i = ; i <= n; i++) {
memset(vis, , sizeof(vis));
for (int j = ; f[j] <= i && j < maxm; j++)
vis[sg[i - f[j]]] = ;
for (int j = ;; j++) if (!vis[j]) { //最小的未出现的正整数
sg[i] = j;
break;
}
}
} //记忆化搜索
//f[]: 从小到大排序
//sg[]: 初始化为-1
//maxm,石子个数,集合的最大数量
int dp(int x)
{
if (sg[x] != -) return sg[x];
bool vis[maxn];
memset(vis, , sizeof(vis));
for (int i = ; i < maxm; i++)
{
if (f[i] <= x)
{
dp(x - f[i]);
vis[sg[x - f[i]]] = ;
}
}
for (int i = ;; i++)
{
if (!vis[i]) return sg[x] = i;
}
} void init()
{
f[] = f[] = ;
for (int i = ; i < maxm; i++)
f[i] = f[i - ] + f[i - ];
memset(sg, -, sizeof(sg));
} int m, n, p; int main()
{
init();
//getSG(1000);
while (scanf("%d%d%d", &n, &m, &p) == && n)
{
if (dp(m) ^ dp(n) ^ dp(p)) printf("Fibo\n");
else printf("Nacci\n");
}
return ;
}

参考链接:

1、https://blog.csdn.net/yizhangbiao/article/details/51992022

2、https://blog.csdn.net/strangedbly/article/details/51137432

SG函数入门&&HDU 1848的更多相关文章

  1. HDU 1848 Fibonacci again and again(SG函数入门)题解

    思路:SG打表 参考:SG函数和SG定理[详解] 代码: #include<queue> #include<cstring> #include<set> #incl ...

  2. SG 函数初步 HDU 1536 &amp;&amp; HDU 1944

    题目链接:http://acm.hdu.edu.cn/showproblem.php? pid=1944 pid=1536"> http://acm.hdu.edu.cn/showpr ...

  3. sg函数入门理解

    首先理解sg函数必须先理解mex函数 mex是求除它集合内的最小大于等于0的整数,例:mex{1,2}=0:mex{2}=0:mex{0,1,2}=3:mex{0,5}=1. 而sg函数是啥呢? 对于 ...

  4. (巴什博弈 sg函数入门1) Brave Game -- hdu -- 1846

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=1846 首先来玩个游戏,引用杭电课件上的: (1) 玩家:2人:(2) 道具:23张扑克牌:(3) 规则: ...

  5. SG函数入门

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

  6. hdu 1848 简单SG函数

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1848 Problem Description 任何一个大学生对菲波那契数列(Fibonacci num ...

  7. hdu 1848 Fibonacci again and again(SG函数)

    Fibonacci again and again HDU - 1848 任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的: F(1)=1; F(2)= ...

  8. HDU 1848 Fibonacci again and again【SG函数】

    对于Nim博弈,任何奇异局势(a,b,c)都有a^b^c=0. 延伸: 任何奇异局势(a1, a2,… an)都满足 a1^a2^…^an=0 首先定义mex(minimal excludant)运算 ...

  9. HDU 1848 Fibonacci again and again (斐波那契博弈SG函数)

    Fibonacci again and again Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & ...

随机推荐

  1. HDU 5546 Ancient Go (搜索)

    题意: Alice和Bob正在下古代围棋,规则如下: 棋盘有8×8个格子,棋子下在棋盘的交叉点上,故可以有9×9个落子的位置 Alice执黑棋Bob执白棋轮流落子 与棋子直线相连的空白交叉点叫做气.当 ...

  2. 51Nod - 1154 回文串划分(最少回文串dp)

    回文串划分 有一个字符串S,求S最少可以被划分为多少个回文串. 例如:abbaabaa,有多种划分方式.   a|bb|aabaa - 3 个回文串 a|bb|a|aba|a - 5 个回文串 a|b ...

  3. react-router-dom

    创建: 2019/05/18  安装 npm install react-router-dom --save    

  4. [sdut] 1400 马的走法 dfs

    Problem Description 在一个4*5的棋盘上,马的初始位置坐标(纵 横)位置由键盘输入,求马能返回初始位置的所有不同走法的总数(马走过的位置不能重复,马走“日”字).如果马的初始位置坐 ...

  5. unity 代码添加AnimationEvent

    经过测试只要Animator跟继承monoBehaviour的类A在同一个节点上,就可以注册类A中的public 方法,含0或1个参数(int,float,object,string),注意:只能是0 ...

  6. shader Category

    Category:是渲染命令的逻辑组,着色器可以多个子着色器,他们需要共同的效果 // Copyright 2014 Google Inc. All rights reserved. // // Li ...

  7. spring-eureka 源码解读----为什么一个服务最多两分钟被其他服务感知

    Eureka的wiki上有一句话,大意是一个服务启动后最长可能需要2分钟时间才能被其它服务感知到,但是文档并没有解释为什么会有这2分钟.其实这是由三处缓存 + 一处延迟造成的. 首先,Eureka对H ...

  8. 2014-9-27 NOIP模拟赛

    1.栅栏迷宫 田野上搭建了一个黄金大神专用的栅栏围成的迷宫.幸运的是,在迷宫的边界上留出了两段栅栏作为迷宫的出口.更幸运的是,所建造的迷宫是一个“完美的”迷宫:即你能从迷宫中的任意一点找到一条走出迷宫 ...

  9. Node.js 内置模块crypto加密模块(5) RSA

    RSA加密算法 写在前面: 了解RSA算法的原理请查看下面的文章 一文搞懂 RSA 算法 来源:简书  作者:somenzz 在使用 Node 进行 RSA 加密之前我们首先需要获取RSA公共和私有密 ...

  10. 日期和时间-time时间模块

    时间的检测 #时间的检测 #导入时间模块 import time #返回当前时区与格林尼治所在时区的相差秒数(推荐) print(time.timezone) #输出结果:-28800 #返回当前时区 ...