标签:scanf cst move ESS moved href com multiple ice
一. 巴什博奕(Bash Game):
A和B一块报数,每人每次报最少1个,最多报4个,看谁先报到30。这应该是最古老的关于巴什博奕的游戏了吧。
其实如果知道原理,这游戏一点运气成分都没有,只和先手后手有关,比如第一次报数,A报k个数,那么B报5-k个数,那么B报数之后问题就变为,A和B一块报数,看谁先报到25了,进而变为20,15,10,5,当到5的时候,不管A怎么报数,最后一个数肯定是B报的,可以看出,作为后手的B在个游戏中是不会输的。
那么如果我们要报n个数,每次最少报一个,最多报m个,我们可以找到这么一个整数k和r,使n=k*(m+1)+r,代入上面的例子我们就可以知道,如果r=0,那么先手必败;否则,先手必胜。
巴什博奕:只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜。
1 #include <iostream> 2 using namespace std; 3 int main() 4 { 5 int n,m; 6 while(cin>>n>>m) 7 if(n%(m+1)==0) cout<<"后手必胜"<<endl; 8 else cout<<"先手必胜"<<endl; 9 return 0; 10 } 11
二. 威佐夫博弈(Wythoff Game):
有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。
直接说结论了,若两堆物品的初始值为(x,y),且x<y,则另z=y-x;
记w=(int)[((sqrt(5)+1)/2)*z ];
若w=x,则先手必败,否则先手必胜。
1 #include <cstdio> 2 #include <cmath> 3 #include <iostream> 4 using namespace std; 5 int main() 6 { 7 int n1,n2,temp; 8 while(cin>>n1>>n2) 9 { 10 if(n1>n2) swap(n1,n2); 11 temp=floor((n2-n1)*(1+sqrt(5.0))/2.0); 12 if(temp==n1) cout<<"后手必胜"<<endl; 13 else cout<<"先手必胜"<<endl; 14 } 15 return 0; 16 } 17 18
三. 尼姆博弈(Nimm Game):
尼姆博弈指的是这样一个博弈游戏:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜。
结论就是:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。
1 #include <cstdio> 2 #include <cmath> 3 #include <iostream> 4 using namespace std; 5 int main() 6 { 7 int n,ans,temp; 8 while(cin>>n) 9 { 10 temp=0; 11 for(int i=0;i<n;i++) 12 { 13 cin>>ans; 14 temp^=ans; 15 } 16 if(temp==0) cout<<"后手必胜"<<endl; 17 else cout<<"先手必胜"<<endl; 18 } 19 return 0; 20 } 21 22
四. 斐波那契博弈:
有一堆物品,两人轮流取物品,先手最少取一个,至多无上限,但不能把物品取完,之后每次取的物品数不能超过上一次取的物品数的二倍且至少为一件,取走最后一件物品的人获胜。
结论是:先手胜当且仅当n不是斐波那契数(n为物品总数)
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 using namespace std; 5 const int N = 55; 6 int f[N]; 7 void Init() 8 { 9 f[0] = f[1] = 1; 10 for(int i=2;i<N;i++) 11 f[i] = f[i-1] + f[i-2]; 12 } 13 int main() 14 { 15 Init(); 16 int n; 17 while(cin>>n) 18 { 19 if(n == 0) break; 20 bool flag = 0; 21 for(int i=0;i<N;i++) 22 { 23 if(f[i] == n) 24 { 25 flag = 1; 26 break; 27 } 28 } 29 if(flag) puts("Second win"); 30 else puts("First win"); 31 } 32 return 0; 33 }
五、SG函数,SG定理
Sprague-Grundy定理(SG定理):
游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。而Bouton定理就是Sprague-Grundy定理在Nim游戏中的直接应用,因为单堆的Nim游戏 SG函数满足 SG(x) = x。对博弈不是很清楚的请参照http://www.cnblogs.com/ECJTUACM-873284962/p/6398385.html进行进一步理解。
SG函数:
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于任意状态 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时。
S
2 1 2 19 20 2 1 19 1 18Sample Output
NO YES
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,sg[1<<21],vis[21]; 4 int dfs(int x) 5 { 6 memset(vis,0,sizeof(vis)); 7 for(int i = 20;i>=0;i--) 8 { 9 if(x & (1<<i)) 10 { 11 int temp = x; 12 for(int j = i-1;j>=0;j--) 13 { 14 if(!(x&(1<<j))) 15 { 16 temp ^= (1<<j)^(1<<i); 17 vis[sg[temp]]=1; 18 break; 19 } 20 } 21 } 22 } 23 for(int i = 0;i<=20;i++) 24 if(!vis[i]) 25 return i; 26 return 0; 27 } 28 int main() 29 { 30 memset(sg,0,sizeof(sg)); 31 for(int i = 0;i<(1<<20);i++) 32 sg[i]=dfs(i); 33 int T; 34 scanf("%d",&T); 35 while(T--) 36 { 37 int n; 38 scanf("%d",&n); 39 int ans = 0; 40 for(int i = 0;i<n;i++) 41 { 42 int res = 0,temp; 43 int q; 44 scanf("%d",&q); 45 while(q--) 46 scanf("%d",&temp),res|=1<<(20-temp); 47 ans^=sg[res]; 48 } 49 if(ans) 50 printf("YES\n"); 51 else 52 printf("NO\n"); 53 } 54 55 }
标签:scanf cst move ESS moved href com multiple ice
原文地址:https://www.cnblogs.com/weixq351/p/9473905.html