[博弈论]
巴什博弈:
只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)*(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。
正确解法:
若 n%(m+1)==0 的话,一定是后手赢。
其他的话,就是先手赢。
kiki's game
Description
Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes the checkerboard game.The size of the chesserboard is n*m.First of all, a coin is placed in the top right corner(1,m). Each time one people can move the coin into the left, the underneath or the left-underneath blank space.The person who can't make a move will lose the game. kiki plays it with ZZ.The game always starts with kiki. If both play perfectly, who will win the game?
Input
Input contains multiple test cases. Each line contains two integer n, m (0<n,m<=2000). The input is terminated when n=0 and m=0.
output
If kiki wins the game printf "Wonderful!", else "What a pity!".
Examples
Input
5 3
5 4
6 6
0 0
Output
What a pity!
Wonderful!
Wonderful!
正确解法:
输入n和m,在一张n*m上的棋盘上,棋子刚开始在(1,m)上。
棋子可以走 左边,下边,左下边的空白处。
kiki先走,若kiki必赢,则输出 “Wonderful!”,否则输出 ”What a pity! “
因为棋子现在在(1,m)处,它最起码要走(n-1)或 (m-1)步
若其中有一个是奇数,那么kiki必赢。
则n和m中有一个是偶数,那么就赢了。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<map> #include<set> #include<vector> #include<queue> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; const int inf=0x7fffffff; const int N=100000+100; const int M=9999999; const ll mod=1000000000+7; int main() { int n,m; while(scanf("%d %d",&n,&m)) { if(n==0) break; if(n%2==0||m%2==0) printf("Wonderful!\n"); else printf("What a pity!\n"); } return 0; }
斐波那契博弈
有一堆个数为n的石子,游戏双方轮流取石子,满足:
1)先手不能在第一次把所有的石子取完;
2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。
约定取走最后一个石子的人为赢家,求必败态。
这个和之前的Wythoff’s Game 和取石子游戏 有一个很大的不同点,就是游戏规则的动态化。之前的规则中,每次可以取的石子的策略集合是基本固定的,但是这次有规则2:一方每次可以取的石子数依赖于对手刚才取的石子数。
这个游戏叫做Fibonacci Nim,肯定和Fibonacci数列:f[n]:1,2,3,5,8,13,21,34,55,89,… 有密切的关系。如果试验一番之后,可以猜测:先手胜当且仅当n不是Fibonacci数。换句话说,必败态构成Fibonacci数列。
就像“Wythoff博弈”需要“Beatty定理”来帮忙一样,这里需要借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。
取石子游戏
Description
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.
output
先取者负输出"Second win". 先取者胜输出"First win".
参看Sample Output.
Examples
Input
2
13
10000
0
Output
Second win
Second win
First win
正确解法:
是斐波那契数列的话,那就是后手赢。不是的话就先手。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<map> #include<set> #include<vector> #include<queue> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; const int inf=0x7fffffff; const int N=100000+100; const int M=9999999; const ll mod=1000000000+7; int main() { ll f[50]; int n; f[0]=2; f[1]=3; for(int i=2;i<=50;i++) f[i]=f[i-1]+f[i-2]; while(scanf("%d",&n)) { if(n==0) break; int flag=0; for(int i=0;i<=50;i++) { if(f[i]==n) { flag=1; printf("Second win\n"); break; } if(f[i]>n) break; } if(flag==0) printf("First win\n"); } return 0; }
威佐夫博弈(Wythoff's game)
取石子游戏
Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
Examples
Input
2 1 8 4 4 7
Output
0 1 0
正确解法:
如果(a,b)是奇异数列,那么一定是后手赢。否则先手赢。
怎么判断是奇异数列呢?
若(b-a)*((sqrt(5)+1)/2)==a的话,那么就是奇异数列。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<map> #include<set> #include<vector> #include<queue> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; const int inf=0x7fffffff; const int N=100000+100; const int M=9999999; const ll mod=1000000000+7; int main() { int a,b; while(scanf("%d %d",&a,&b)!=EOF) { if(a>b) swap(a,b); int c=floor((b-a)*((sqrt(5)+1)/2)); if(a==c) cout<<0<<endl; else cout<<1<<endl; } return 0; }
Good Luck in CET-4 Everybody!
Description
大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此。当然,作为在考场浸润了十几载的当代大学生,Kiki和Cici更懂得考前的放松,所谓“张弛有道”就是这个意思。这不,Kiki和Cici在每天晚上休息之前都要玩一会儿扑克牌以放松神经。
“升级”?“双扣”?“红五”?还是“斗地主”?
当然都不是!那多俗啊~
作为计算机学院的学生,Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?
当然,打牌无论谁赢都问题不大,重要的是马上到来的CET-4能有好的状态。
Good luck in CET-4 everybody!
Input
输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。
output
如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。
Examples
Input
1
3
Output
正确解法:
sg函数!
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<map> 6 #include<set> 7 #include<vector> 8 #include<queue> 9 #include<algorithm> 10 #include<cmath> 11 using namespace std; 12 typedef long long ll; 13 const int inf=0x7fffffff; 14 const int N=100000+100; 15 const int M=9999999; 16 const ll mod=1000000000+7; 17 int a[15],n; 18 int sg[1005]; 19 int mex(int x) 20 { 21 if(sg[x]!=-1) return sg[x]; 22 int bok[1005]; 23 memset(bok,0,sizeof(bok)); 24 for(int i=0;i<10;i++) 25 { 26 int temp=x-a[i]; 27 if(temp<0) break; 28 sg[temp]=mex(temp); 29 bok[sg[temp]]=1; 30 } 31 for(int i=0;;i++) 32 if(!bok[i]) 33 { sg[x]=i; 34 break; 35 } 36 return sg[x]; 37 38 } 39 int main() 40 { 41 a[0]=1; 42 for(int i=1;i<10;i++) 43 a[i]=a[i-1]*2; 44 //memset(sg,-1,sizeof(sg)); 45 while(scanf("%d",&n)!=EOF) 46 { 47 memset(sg,-1,sizeof(sg)); 48 if(mex(n)) printf("Kiki\n"); 49 else 50 printf("Cici\n"); 51 52 } 53 54 return 0; 55 }
1 int mex(int x) 2 { 3 if(sg[x]!=-1) return sg[x]; 4 int bok[1005]; 5 memset(bok,0,sizeof(bok)); 6 for(int i=0;i<10;i++) 7 { 8 int temp=x-a[i]; 9 if(temp<0) break; 10 sg[temp]=mex(temp); 11 bok[sg[temp]]=1; 12 } 13 for(int i=0;;i++) 14 if(!bok[i]) 15 { sg[x]=i; 16 break; 17 } 18 return sg[x]; 19 }
硬币游戏
Description
给定K个数字 a1,a2—ak ,有x个硬币,Alice 和Bob 交替拿走硬币,拿走的个数只能在 ai 中选,求最后谁赢了。
Input
x 「1,10000」
k 「1,100」
ai 「1,x」
output
如果Alice能赢的话,请输出“Alice”,否则请输出“Bob”,每个实例的输出占一行。
Examples
Input
9
2
1 4
Output
正确解法:
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<map> 6 #include<set> 7 #include<vector> 8 #include<queue> 9 #include<algorithm> 10 #include<cmath> 11 using namespace std; 12 typedef long long ll; 13 const int inf=0x7fffffff; 14 const int N=1000000+100; 15 const int M=9999999; 16 const ll mod=1000000000+7; 17 int x,k,a[110]; 18 int sg[110]; 19 int mex(int x) 20 { 21 if(sg[x]!=-1) return sg[x]; 22 bool flag[110]; 23 for(int i=1;i<=k;i++) 24 { 25 int temp=x-a[i]; 26 if(temp<0) break; 27 sg[temp]=mex(temp); 28 flag[sg[temp]]=1; 29 } 30 for(int i=0;;i++) 31 { 32 if(flag[i]==0) 33 { 34 sg[x]=i; 35 break; 36 } 37 } 38 return sg[x]; 39 } 40 int main() 41 { 42 scanf("%d",&x); 43 scanf("%d",&k); 44 for(int i=1;i<=k;i++) 45 scanf("%d",&a[i]); 46 memset(sg,-1,sizeof(sg)); 47 if(mex(x)==0) 48 cout<<"Bob"<<endl; 49 else 50 cout<<"Alice"<<endl; 51 52 53 54 return 0; 55 }