[博弈论]hiho#1173 : 博弈游戏·Nim游戏·三
2015-06-04 22:01
225 查看
首先给出基本定义:
对于一个游戏可能发生的局面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=x[1..n],每一次我们只能改变其中一个单一游戏的局面。则其总局面的sg值等于这些单一游戏的sg值异或和。
即:
sg(X) = sg(x[1]) xor sg(x[2]) xor … xor sg(x
)
要证明这一点我们只要证明:
(1) 假设sg(x[1]) xor sg(x[2]) xor … xor sg(x
) = A,对于任意一个0 <= B < A,总存在一个X的后续局面Y,使得sg(Y) = B。
(2) 假设sg(x[1]) xor sg(x[2]) xor … xor sg(x
) = A,不存在一个X的后续局面Y,使得sg(Y) = A。
这是hiho里给出的解释,首先给出了定理,然后解释了定理并证明了,然后结合问题分析了问题应该怎么做。标准的中国式例题讲解思维。确实这样的逻辑会比较符合大多数人的思维。但是这里面是否又有些值得深思的呢?
代码就是依据定理来写的,求出所有的sg[].
另外,对于这个题每个sg值就有两种可能:
(1)不分堆:石子数量为k’=0..k-1,则sg(k’)
(2)分堆:石子变为2堆,数量为(1,k-1),(2,k-2),…,(k-1,1)。设第一堆的石子数量为i,则sg值为sg(i) xor sg(k-i)。(这里用到了sg定理)
那么可以推算出sg(k) = mex{sg(0), sg(i), sg(i) xor sg(k - i) | i = 1..k-1}。
对于一个游戏可能发生的局面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=x[1..n],每一次我们只能改变其中一个单一游戏的局面。则其总局面的sg值等于这些单一游戏的sg值异或和。
即:
sg(X) = sg(x[1]) xor sg(x[2]) xor … xor sg(x
)
要证明这一点我们只要证明:
(1) 假设sg(x[1]) xor sg(x[2]) xor … xor sg(x
) = A,对于任意一个0 <= B < A,总存在一个X的后续局面Y,使得sg(Y) = B。
(2) 假设sg(x[1]) xor sg(x[2]) xor … xor sg(x
) = A,不存在一个X的后续局面Y,使得sg(Y) = A。
这是hiho里给出的解释,首先给出了定理,然后解释了定理并证明了,然后结合问题分析了问题应该怎么做。标准的中国式例题讲解思维。确实这样的逻辑会比较符合大多数人的思维。但是这里面是否又有些值得深思的呢?
代码就是依据定理来写的,求出所有的sg[].
另外,对于这个题每个sg值就有两种可能:
(1)不分堆:石子数量为k’=0..k-1,则sg(k’)
(2)分堆:石子变为2堆,数量为(1,k-1),(2,k-2),…,(k-1,1)。设第一堆的石子数量为i,则sg值为sg(i) xor sg(k-i)。(这里用到了sg定理)
那么可以推算出sg(k) = mex{sg(0), sg(i), sg(i) xor sg(k - i) | i = 1..k-1}。
[code]#include <cstdio> #include <cstring> #include <cmath> #include <iostream> #include <vector> #include <map> #include <set> #include <queue> #include <algorithm> #define read freopen("q.in","r",stdin) #define LL __int64 #define maxn 20004 #define inf 10000000 #define mod 142857 using namespace std; int sg[maxn],a[maxn]; bool vis[maxn]; void Accepted() { int i,j; sg[0]=0; for(i=1;i<=maxn;i++) { memset(vis,0,sizeof(vis)); for(j=0;j<i;j++) { int t=sg[j]^sg[i-j]; vis[sg[j]]=1; vis[t]=1; } for(j=0;j<maxn;j++)if(!vis[j]) { sg[i]=j; break; } } } int main() { int n,i,j,x; Accepted(); scanf("%d",&n); int res; scanf("%d",&x); res=sg[x]; for(i=1;i<n;i++) { scanf("%d",&x); res^=sg[x]; } if(!res)cout<<"Bob"<<endl; else cout<<"Alice"<<endl; }
相关文章推荐
- 今天执行grep命令差点把服务器搞崩
- CvRect、RECT和Rect
- Hibernate 对象的三种状态 transient persistent detached
- 汇编语言学习笔记(3)——寄存器,内存访问
- EPUB弹出窗口式脚注
- webapp 外部css的引用
- C#代码实现邮箱验证C#中及一些常用的正则表达式
- 实验三 敏捷开发与XP实践(改)
- Java NIO--初步认识
- JavaScript学习笔记(二)
- Java常用的 API
- [leetcode] Maximal Square
- 山一程,水一程,身向榆关那畔行,夜深千帐灯。
- C/C++字符串与字符数组
- 网络通信
- JAVA方法值传递OR引用传递
- 2015/6/4 深圳--存钱罐(存钱网)
- 从哪里开始
- SpeechClassifier API 与端点检测
- P124.45