用SJ定理解决Anti-SG游戏
2015-09-04 10:08
323 查看
Anti-SG游戏定义
1、决策集合为空的操作者胜。2、其余规则与SG游戏一致。
SJ定理
对于任意一个Anti-SG游戏,如果定义所有子游戏的SG值为0时游戏结束,先手必胜的条件:1、游戏的SG值为0且所有子游戏SG值均不超过1。
2、游戏的SG值不为0且至少一个子游戏SG值超过1。
证明
先证明第一个条件:所有都不超过1,那么显然如果有偶数个1则先手必胜偶数个1即游戏的SG值为0。
再证明第二个条件:
如果有至少两个子游戏SG值超过1,然后其余有偶数个1。那么游戏的SG值就为所有SG值超过1的子游戏的SG值的Nim和。设为X。假设X中1的最高位为第k位,那么一定存在至少一个子游戏的SG值第k位有1,设这个子游戏的SG值为Y。那么我们可以把这个子游戏变为YxorX。可以知道YxorX<Y,因为会消去第k位上的1,k位之后不变,那么相当于减少了一个2k−1,前k-1位不论如何变,最多增加2k−1−1。根据SG函数的转移可以得知一定能转移到一个决策其SG值为YxorX。接下来游戏的SG值便变为0,而此时至少存在一个子游戏的SG值超过1。此条件下先手必胜。
如果有至少两个子游戏的SG值超过1,然后其余有奇数个1。我们设所有SG值超过1的子游戏的SG值的Nim和为X。那么游戏的SG值为Xxor1。因为不为0,那么现在如果X=0,我们去掉一个1即可。于是变为游戏的SG值为0,而此时至少存在两个子游戏的SG值超过1。如果X不为0,那么最高位的1一定不在第一位。于是和1没有关系,像上面一样处理即可。
如果只有一个子游戏的SG值超过1,然后其余有偶数个1。显然我们可以拿将那个超过1的子游戏转移到一个决策使SG值为1,就能造成奇数个1。
如果只有一个子游戏的SG值超过1,然后其余有奇数个1。直接将那个超过1的子游戏转移到一个决策使SG值为0,就能造成奇数个1。
这样证明了先手必胜局面有至少一个后继局面为先手必败局面。
接下来需证明每一个先手必败局面都只能转移到先手必胜局面。
先来证明第二个条件:
所有SG值不超过1,那么有奇数个1先手就必败,无论如何都只能转移到有偶数个1的情况。有奇数个1即游戏的SG值不为0。
第一个条件:
如果有至少两个子游戏的SG值超过1,那么无论如何都会转移到一个先手必胜局面(有至少一个SG值超过1,游戏的SG值不为0),因为无法造成游戏的SG值继续为0。
如果只有一个子游戏的SG值超过1,这是不可能发生的,不可能存在一个局面只有一个子游戏SG值超过1但异或和为0的,一定有至少两个子游戏SG值超过1。
证毕。
习题
【JZOJ1007,SHOI2008】小约翰的游戏。
小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有n堆石子,小约翰和他的哥哥轮流取石子,每个人取的时候,可以随意选择一堆石子,在这堆石子中取走任意多的石子,但不能一粒石子也不取,我们规定取到最后一粒石子的人算输。小约翰相当固执,他坚持认为先取的人有很大的优势,所以他总是先取石子,而他的哥哥就聪明多了,他从来没有在游戏中犯过错误。小约翰一怒之前请你来做他的参谋。自然,你应该先写一个程序,预测一下谁将获得游戏的胜利。
每堆石子数不超过5000,一共最多500组数据,n<=50。
设sg[i]表示一堆石子含有i个石头的估价函数。显然
sg[i]=mexj<i{sg[j]}
我们算出sg函数,然后用SJ定理解决此题。
复杂度O(50002+T∗N)
但实际上,可以发现sg[i]=i。
可以省去转移的复杂度。
#include<cstdio> #define fo(i,a,b) for(i=a;i<=b;i++) using namespace std; bool czy; int i,j,k,l,t,n,m,ca; int main(){ scanf("%d",&ca); while (ca--,ca>=0){ scanf("%d",&n); czy=0; l=0; fo(i,1,n){ scanf("%d",&t); l^=t; if (t>1) czy=1; } if (!czy){ if (n%2) printf("Brother\n");else printf("John\n"); } else { if (l) printf("John\n");else printf("Brother\n"); } } }
相关文章推荐
- 堆的插入、删除和建立操作,堆排序
- js calendar橙色日期选择器代码
- 网路基础(python)
- 面试题———关于将一个整型数组中的所有元素组合成一个数字,并找出最小一个。
- C++ 中使用字符数组来存放未知长度的字符串
- hud 2795 Billboard || HDOJ 2009 Summer Exercise(5) || 线段树
- TQ2440 学习笔记—— 23、通用异步收发器 UART
- 知识整理记载
- Java中break跳出多层循环
- 打造自己的注解框架
- JPA实现的CRUD
- 几本京东上的CISCO云计算相关书籍
- LeetCode——H-Index
- 【小熊刷题】sqrt(x) <Leetcode 69 Java>
- Mysql varchar型自增方法
- LINUX路由表:查看、设置及操作权限。
- 完全教程 Aircrack-ng破解WEP、WPA-PSK加密利器
- Programming Ability Test学习 1029. 旧键盘(20)
- TQ2440 学习笔记—— 22、系统时钟和定时器
- 精通Hibernate——Hibernate的检索策略