1、巴什博弈
题意:只有一堆物品,物品的个数为n,每次取1~m个,最后取完者胜。
解题思路:我们可以这样思考,对手最多只能拿m个,最少拿1个,那么我们就去构造m+1个,这样不管对手怎么取,都不可能一次性取完,所以m+1为必败点。
#include <iostream>
#include <math.h>
#include <algorithm>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
if(n%(m+1)==0)cout<<"second"<<endl;
else cout<<"first"<<endl;
}
return 0;
}
2、Fibonacci博弈
题意:1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
解题思路:打表找规律
n = 2时输出second;
n = 3时也是输出second;
n = 4时,第一个人想获胜就必须先拿1个,这时剩余的石子数为3,此时无论第二个人如何取,第一个人都能赢,输出first;
n = 5时,first不可能获胜,因为他取2时,second直接取掉剩下的3个就会获胜,当他取1时,这样就变成了n为4的情形,所以输出的是second;
n = 6时,first只要去掉1个,就可以让局势变成n为5的情形,所以输出的是first;
n = 7时,first取掉2个,局势变成n为5的情形,故first赢,所以输出的是first;
n = 8时,当first取1的时候,局势变为7的情形,第二个人可赢,first取2的时候,局势变成n为6得到情形,也是第二个人赢,取3的时候,second直接取掉剩下的5个,所以n = 8时,输出的是second;
…………
从上面的分析可以看出,n为2、3、5、8时,这些都是输出second,即必败点,仔细的人会发现这些满足斐波那契数的规律,可以推断13也是一个必败点。
我们打表找规律不难发现这是Fibonacci序列,但如果我们对Fibonacci序列十分了解,题意中的2倍也能使我们的思路向Fibonacci序列上靠。因为序列中每相邻的三个数,第三个数肯定是小于3倍的第一个数的。
#include<iostream>
using namespace std;
int main()
{
int fib[50] = {2,3};
for(int i = 2; i < 45; i++)
fib[i] = fib[i - 1] + fib[i - 2];
int n;
while(cin>>n)
{
if(n==0)break;
int flag = 1;
for(int i = 0; i < 44; i++)
if(fib[i] == n){
flag = 0;
break;
}
if(flag)cout<<"First win"<<endl;
else cout<<"Second win"<<endl;
}
return 0;
}
3、威佐夫博弈
题意:有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
解题思路:这种博弈与其去分析奇异局势,还不如直接去记公式。假设两堆石子数分别为a,b;如果a=abs(a-b)*(sqrt(5)+1)/2,那么此组数为必败组。
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
long long n,m;
while(cin>>n>>m)
{
if(n>m)swap(n,m);
int x=m-n;
double yy=(1.0+sqrt(5.0))/2;
if(n==int(x*yy))cout<<"0"<<endl;
else cout<<"1"<<endl;
}
return 0;
}
4、尼姆博弈(非常重要,因为有很多根据它延申的题)
题意:m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取时可以从有8个的那一堆取走7个剩下1个,也可以从有9个的中那一堆取走9个剩下0个,也可以从有10个的中那一堆取走7个剩下3个.
解题思路:我们先来看两堆的情况,如果两堆的个数相等的话,那么不管先手的人怎么取,先手必输。这不难理解,那当m>2的时候呢??我们同样可以比拟两堆的情况把前两堆看作一堆,就变成了前两堆与第三堆的比较 (注意:此时不是比较前两堆的个数与第三堆的个数,而是前两堆取完石子后的情况与第三堆比较),以此类推。这样我们就引入了异或运算。
运算规则
相同为0,不同为1,即
1 ^ 1 = 0
0 ^ 0 = 0
1 ^ 0 = 1
异或性质
(1)交换律: A ^ B = B ^ A
(2)结合律: ( A ^ B ) ^ C = A ^ ( B ^ C )
(3)自反性: A ^ B ^ B = A (由结合律可推: A ^ B ^ B = A ^ ( B ^ B ) = A ^ 0 = A)
#include <iostream>
using namespace std;
int main()
{
int n;
int x[200005];
while(cin>>n)
{
if(n==0)break;
int temp=0;
int s;
for(int a=0;a<n;a++)
{
cin>>x[a];
temp^=x[a];
}
if(temp!=0)
{
cout<<"Yes"<<endl;
for(int a=0;a<n;a++)
{
s=temp^x[a];
if(s<x[a])
cout<<x[a]<<" "<<s<<endl;
}
}
else cout<<"No"<<endl;
}
return 0;
}