参考链接:
http://blog.sina.com.cn/s/blog_51cea4040100h3l9.html
这篇主要就是讲anti-sg、multi-sg和every-sg的。
例1 poj3537
有一个长度为n的一维棋盘,两人轮流下子,如果一个人下了连在一起的三个子就立刻赢了,如果一个人下不了子了他就输了。3<=n<=2000
我们可以发现,如果我们在第i个地方落子,游戏就被分解为了两个子游戏:长度为i-3和n-i-2的两个子游戏。(至于为什么我也不好解释啊,就是如果之后都在这些地方下那么肯定i位置不会连成3子)
这样分解游戏的模型叫做multi-sg,它的sg函数定义是没什么区别的,可以直接刚。
2000的话平方裸搜就可以了。
//By zzq #include <iostream> #include <stdio.h> #include <string.h> using namespace std; int sg[2333]; int tmd=0; int gsg(int n) { if(n<0) return 0; if(sg[n]>=0) return sg[n]; bool ff[2001]={}; for(int i=1;i<=n;i++) ff[gsg(i-3)^gsg(n-i-2)]=1; for(int i=0;;i++) { if(!ff[i]) return sg[n]=i; } } int main() { memset(sg,-1,sizeof(sg)); int n; scanf("%d",&n); if(gsg(n)) puts("1"); else puts("2"); }
例2 bzoj1022
nim游戏,取到最后一粒石子的人输。
多组数据,n<=5000。
这个东西我们可以发现除了取到最后一粒石子的人输以外都是沙茶sg,所以我们就叫它anti-sg。
接下来就是结论啦,贾志豪神犇发明了一个sj定理可以解决这个问题:
对于任意一个anti-sg游戏,先手必胜当且仅当:
游戏的sg函数不为0且游戏中某个单一游戏的sg函数大于1;
游戏的sg函数为0且游戏中没有单一游戏的sg函数大于1。
这种结论题就直接做就是了。
//By zzq #include <iostream> #include <stdio.h> #include <string.h> using namespace std; int T,n; int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); bool dy1=0; int ans=0; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); ans^=x; dy1|=x>1; } if(((bool)ans)==dy1) puts("John"); else puts("Brother"); } }
every-sg没什么靠谱的例题啊...待补