SG函数

SG函数

对于任意状态 x , 定义 SG(x) = mex(S),其中 S 是 x 后继状态的 SG 函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么 SG(x)=mex{SG(a),SG(b),SG(c)}。 这样集合 S 的终态必然是空集,所以SG函数的终态为 SG(x)=0,当且仅当 x 为必败点P时。
注:
mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
SG 定理:游戏和的SG函数等于各个游戏SG函数的Nim和。
Nim和 :各个数相异或的结果

//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
int f[N],SG[MAXN],S[MAXN];
void  getSG(int n){
    int i,j;
    memset(SG,0,sizeof(SG));
    //因为SG[0]始终等于0,所以i从1开始
    for(i = 1; i <= n; i++){
        //每一次都要将上一状态 的 后继集合 重置
        memset(S,0,sizeof(S));
        for(j = 0; f[j] <= i && j <= N; j++)
            S[SG[i-f[j]]] = 1;  //将后继状态的SG函数值进行标记
        for(j = 0;; j++) if(!S[j]){   //查询当前后继状态SG值中最小的非零值
            SG[i] = j;
            break;
        }
    }
}

尼姆博弈(Nimm Game):

尼姆博弈指的是这样一个博弈游戏:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜。
结论:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。
SG定理:
图片说明 ,先手必胜。
图片说明 ,先手必败。

新玩法

  1. 取到最后一件物品的人失败。
    结论:当每堆石子数量都不超过 1 时,n 为奇数先手必败。剩下的就和尼姆博奕的结论一致。
  2. 可以把一堆石子分成不相等的两堆,不能操作为败
void getSG(int n) {
//    memset(sg, 0, sizeof(sg));
    for(int i = 1; i <= n; ++ i) {
        memset(s, 0, sizeof(s));                            //S[]:为x后继状态的集合
        for(int j = 1; j*2 < i; ++ j) s[sg[j]^sg[i-j]] = 1; // sg定理
        while(s[sg[i]]) ++sg[i];                            // mex(...)
    }
}
  1. 尼姆博奕先手必胜,第一步有几种走法。
    结论:把一堆石头移出游戏,去掉一部分再加入游戏,看此时 sg 是否能为零,即 (ans^a[i]) < a[i]。
    long long ans = 0;
    for(int i = 1; i <= n; ++ i) {
        scanf("%lld", &a[i]);
        ans ^= a[i];
    }
    int res = 0;
    for(int i = 1; i <= n; ++ i) {
        if ((ans^a[i]) < a[i]) res ++;
    }

巴什博弈

只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取 1 个,最多取 m 个,最后取光者得胜。
结论:若 n % (m+1) == 0,则先手必败,否则先手必胜

高级玩法

取石子的数量,不是 1~m 内的连续数字,而是只能在数组 a 中选。
方法一:定义 P 为先手必败,N 为先手必胜,P 的后继全是 N,N 的后继包含 P,依次类推寻找周期即可。
方法二:sg函数打表。

void getSG(int n, int t[], int m) { // 石块数量,拿去规则
//    memset(sg, 0, sizeof(sg));
    for(int i = 1; i <= n; ++ i) {
        memset(s, 0, sizeof(s));                                      //S[]:为x后继状态的集合
        for(int j = 1; t[j] <= i && j <= m; ++ j) s[sg[i-t[j]]] = 1;  //将后继状态的SG函数值进行标记
        while(s[sg[i]]) ++sg[i];                           // mex(...)
    }
}

威佐夫博奕(Wythoff's game)

有两堆各若干个物品,两个人轮流从任意一堆中取出至少一个或者同时从两堆中取出同样多的物品,规定每次至少取一个,至多不限,最后取光者胜利。
结论:若 a == int((b-a)*1.618),则先手必败,否则先手必胜。

斐波那契数列

只有一堆n个物品,两个人轮流从这堆物品中取物,规则是先手不能在第一次把所有的石子取完,之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。约定取走最后一个石子的人为赢家。
结论:若 n 为斐波那契数,先手必败,否则先手必胜。

数学 文章被收录于专栏

关于acm竞赛数论的个人笔记

全部评论

相关推荐

从大一开始就开始学习Java,一路走来真的不算容易,每次面试都被压力,不过这次终于达成了自己的一大心愿!时间线和面经:8.17-投递9.1-一面实习+项目拷打看门狗机制讲一下redis加锁解锁的本身操作是什么Lua脚本是干什么的udp和tcp讲一下流量控制讲一下令牌桶算法说一下大端和小端是什么线程和协程有什么区别怎么切换协程切换的时候具体做了什么对于程序来说,你刚才提到的保存和恢复现场,这个现场有哪些信息udp优势现在有一个客户端和服务端,要实现TCP的通信,我们的代码要怎么写服务器怎么感知有新的连接怎么处理多个客户端的请求连接TCP怎么处理粘包和分包现在有两个文件,然后每个文件都有一亿条URL,每个的长度都很长,要怎么快速查找这两个文件共有的URLHashmap底层说一下怎么尽量提升插入和查询的效率如果要查找快,查询快,还有解决非空的问题,怎么做LoadingCache了解吗手撕:堆排序9.4-二面部门的leader,超级压力面拷打实习+项目,被喷完全没东西类的加载到垃圾回收整个底层原理讲一遍类加载谁来执行类加载器是什么东西,和进程的关系Java虚拟机是什么东西,和进程的关系如果我们要执行hello&nbsp;world,那虚拟机干了什么呢谁把字节码翻译成机器码,操作时机是什么Java虚拟机是一个执行单元吗Java虚拟机和操作系统的关系到底什么,假如我是个完全不懂技术的人,举例说明让我明白一个操作系统有两个Java程序的话,有几个虚拟机有没有单独的JVM进程存在启动一个hello&nbsp;world编译的时候,有几个进程JVM什么时候启动比如执行一条Java命令的时候对应一个进程,然后这个JVM虚拟机到底是不是在这个进程里面,还是说要先启动一个JVM虚拟机的进程垃圾回收机制的时机能手动触发垃圾回收吗垃圾回收会抢占业务代码的CPU吗垃圾回收算法简单说说垃圾回收机制的stop&nbsp;the&nbsp;world存在于哪些时机垃圾回收中的计算Region的时候怎么和业务代码并行执行假如只有一个线程,怎么实现并行Java为什么要这么实现Java效率比C++慢很多,那为什么还要这样实现Java虚拟机到底是什么形式存在的说一下Java和C++的区别还有你对Java设计理念的理解无手撕面试结束的时候,我真的汗流浃背了,面试官还和我道歉,说他是故意压力面想看看我的反应的,还对我给予了高度评价:我当面试官这么多年,你是我见过最好的一个9.9-三面临时通知的加面,就问了三十分钟项目9.11-hr面问过往经历,未来计划,想从腾讯实习中得到什么?当场告知leader十分满意我,所以直接ochr面完一分钟官网流程变成录用评估中,30分钟后mt加微信告知offer正在审批9.15-offer这一次腾讯面试体验真的不错,每个面试官能感觉到专业能力很强,反馈很足,比起隔壁某节真是好太多以后就是鹅孝子了
三本咋了:当面试官这么多年你是我见过的最好的一个
你面试被问到过哪些不会的...
点赞 评论 收藏
分享
迷茫的大四🐶:哇靠,哥们,啥认证啊,副总裁实习,这么有实力嘛
一起聊美团
点赞 评论 收藏
分享
想出去带家人放松顺便自己也休息下,但怕有时间冲突
白火同学:如果国庆真有面试,那国庆都不放假的公司,未免也太压榨了,还是避坑吧。
我的秋招日记
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务