博弈论
一.斐波那契博弈
Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.
Output
先取者负输出"Second win". 先取者胜输出"First win".
Sample Input
2 13 10000 0
Sample Output
Second win Second win First win
解题思路:
从PN图入手,我们会发现如果是斐波那契数先手必输,后手胜利。
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20……
p p n p n n p n n n n p n n n n n n n……
我这里直接利用斐波那契博弈的结论,不能给出证明,下面是可以参考的
证明
https://blog.csdn.net/dgq8211/article/details/7602807
用第二数学归纳法证明:
为了方便,我们将n记为f[i]。
1、当i=2时,先手只能取1颗,显然必败,结论成立。
2、假设当i<=k时,结论成立。
则当i=k+1时,f[i] = f[k]+f[k-1]。
则我们可以把这一堆石子看成两堆,简称k堆和k-1堆。
(一定可以看成两堆,因为假如先手第一次取的石子数大于或等于f[k-1],则后手可以直接取完f[k],因为f[k] < 2*f[k-1])
对于k-1堆,由假设可知,不论先手怎样取,后手总能取到最后一颗。下面我们分析一下后手最后取的石子数x的情况。
如果先手第一次取的石子数y>=f[k-1]/3,则这小堆所剩的石子数小于2y,即后手可以直接取完,此时x=f[k-1]-y,则x<=2/3*f[k-1]。
我们来比较一下2/3*f[k-1]与1/2*f[k]的大小。即4*f[k-1]与3*f[k]的大小,对两值作差后不难得出,后者大。
所以我们得到,x<1/2*f[k]。
即后手取完k-1堆后,先手不能一下取完k堆,所以游戏规则没有改变,则由假设可知,对于k堆,后手仍能取到最后一颗,所以后手必胜。
即i=k+1时,结论依然成立。
那么,当n不是Fibonacci数的时候,情况又是怎样的呢?
这里需要借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。
关于这个定理的证明,感兴趣的同学可以在网上搜索相关资料,这里不再详述。
分解的时候,要取尽量大的Fibonacci数。
比如分解85:85在55和89之间,于是可以写成85=55+30,然后继续分解30,30在21和34之间,所以可以写成30=21+9,
依此类推,最后分解成85=55+21+8+1。
则我们可以把n写成 n = f[a1]+f[a2]+……+f[ap]。(a1>a2>……>ap)
我们令先手先取完f[ap],即最小的这一堆。由于各个f之间不连续,则a(p-1) > ap + 1,则有f[a(p-1)] > 2*f[ap]。即后手只能取f[a(p-1)]这一堆,且不能一次取完。
此时后手相当于面临这个子游戏(只有f[a(p-1)]这一堆石子,且后手先取)的必败态,即先手一定可以取到这一堆的最后一颗石子。
同理可知,对于以后的每一堆,先手都可以取到这一堆的最后一颗石子,从而获得游戏的胜利。
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 using namespace std; 5 const int N = 55;///参看斐波那契表,第50左右时已经超过2^32 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) 30 puts("Second win"); 31 else 32 puts("First win"); 33 } 34 return 0; 35 }
二.威佐夫博弈(Wythoff Game)
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
Sample Input
2 1 8 4 4 7
Sample Output
0 1 0
解题思路:直接给出结论,没有给出证明。
若两堆物品的初始值为(x,y),且x<y,则另z=y-x;记w=(int)[((sqrt(5)+1)/2)*z ];若w=x,则先手必败,否则先手必胜。
1 #include<stdio.h> 2 #include<algorithm> 3 #include<stdlib.h> 4 #include<math.h> 5 using namespace std; 6 int main() 7 { 8 int n,m,k,w,min; 9 while(scanf("%d%d",&n,&m)!=EOF) 10 { 11 if(n>m) 12 { 13 k=n-m; 14 min=m; 15 } 16 else 17 { 18 k=m-n; 19 min=n; 20 } 21 w=(int)((sqrt(5)+1)/2*k); 22 if(w==min) 23 { 24 printf("0\n"); 25 } 26 else 27 { 28 printf("1\n"); 29 } 30 31 } 32 return 0; 33 }
三.巴什博奕(Bash Game)
InputThere are multiple test cases. For each test case, there will be one line of input having two integers N (0 < N <= 10^8) and k (0 < k <= 100). Input terminates when both N and k are zero.
OutputFor each case, print the winner's name in a single line.
Sample Input
1 1 30 3 10 2 0 0
Sample Output
Jiang Tang Jiang
题目意思:唐和江两个人轮流在白板上写数,所写的数字在1-k之间,谁所写的数与之前所写的数的和先超过n,那么谁就会输。
解题思路:巴什博奕的简单变形,原来的问题模型是:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个,最后取光者得胜。
解决思路:当n=m+1时,由于一次最多只能取m个,所以无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜,所以当一方面对的局势是n%(m+1)=0时,其面临的是必败的局势。所以当n=(m+1)*r+s,(r为任意自然数,s≤m)时,如果先取者要拿走s个物品,如果后取者拿走x(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。、变形:条件不变,改为最后取光的人输。
结论:当(n-1)%(m+1)==0时后手胜利。