1.巴什博弈
问题描述一般为,有一堆物品,有N个,A、B轮流从中取物,最少取一个,最多取m个,规定取走最后一堆的人获胜。
对于博弈问题,首先需要分析的是它的必败情况。由题目可知,假设当前的人A面对的物品堆已空,即为0时,就输了,所以最基本的必败为0。那么根据取物品的取值为1~m,可以知道,在上一步B取物的时候,那一堆只剩下1~m个了,根据两人都采取最优策略的原则,A在可能的情况下绝对不会让B遇到1~m这种情况,除非当前A遇到的那一堆是1+m个的,而自己必须至少取一个,才会导致这种局面。换言之,如果初始的时候,N%(m+1)==0,那么每当先手取走k,后手都可以通过取m+1-k使得先手再次面临N%(m+1)==0的必败局面。因此只要N%(m+1)==0 则先手必败,否则 当N%(m+1)==k 【通过取余可知 k<=m】时,先手都可以通过取走k个使得后手面临N%(m+1)==0的必败局面。
一个类似的变相问题:两个人轮流报数,每次至少报一个,至多报m个,报到第N个者胜。
推荐:hdu 1846,2147,2149,2188。
P/N图分析: P表示必败点 遇到该点局势的人必败 N表示必胜点 遇到该点局势的人必胜 注:前提是两者都不失误
对于P点来说,他的每一个下一个承接状态都是必胜点N。对于N点来书,他的每一个下一个承接状态中至少有一个P。
那么图的终结点,也就是结束状态,为P的,因为无论谁面对这个状态都是输。然后逆向推导即可。注意从边界开始推。
2.斐波那契博弈
一堆个数为N的石子,轮流取石。
规定:1.先手不能一次取完
2.如果上一个人取了x,那么现在这个人可以取1·~2*x
介绍一个定理:齐肯多夫定理:任何正整数都可以被分解为任意个不连续(注意不连续很重要)的斐波那契数之和。
对于斐波那契来说,F[i]=F[i-1]+F[i-2],那么如果是两个不连续的斐波那契的话,也就是最接近的情况为F[i]和F[i-2],而F[i]=F[i-1]+F[i-2],因为F[i-1]>F[i-2],所以F[i]>2*F[i-2];这里出现了两倍,于是联想到了斐波那契博弈的要求,取的数量为1~2*x。(解释乱七八糟:暂时自己也想不通,先记一下结论)
所以将一开始的整数分解为若干个不连续的斐波那契数列和(分成多个堆),由于不连续斐波那契的F[i]>2*F[i-2]关系,因此如果一开始的数是一个斐波那契数的话,那么A必须是在后手才能取走最后一个。而如果他不是一个斐波那契数的话,那么A可以先手取走K使得对手面临一个斐波那契数。
因此结论:如果一开始是斐波那契,则后手赢,如果一开始不是斐波那契,则先手赢。即面对斐波那契数的人必输。
3.威佐夫博弈 证明需要用到betty定理
问题描述:有两堆物品,各为若干个,两个轮流任意从其中一堆或者同时从两堆中取相同的任意个(可以至少把一堆取完)。每次至少取一个,取光者胜。
分析:用(ai,bi)表示局势(两堆各自的数量),当A面对(0,0)时,A必输,称此为奇异局势。列举出前几个奇异局势,为(0,0),(1,2)
(3,5),(4,7),(5,9)
规律为 ai为之前的局势中没有出现的第一个数,bi=ai+i。
奇异局势有的性质:
1.任何自然数仅仅包含在一个奇异局势中
ak>ak-1 由于 bk=ak+k>ak-1+k-1=bk-1>ak-1
2.任意操作都可以将奇异局势变为非奇异局势
1)如果当前局势为(x,y) 如果选择改变其中一个分量,那么由于另外一个分量不变,所以不可能变为另外一个奇异局势。
2)如果x,y同时改变,由于其差值不变,因此也不会变为另一个奇异局势。
3.用适当的方法可以将非奇异局势变为奇异局势
结论:对于一个奇异局势(ak,bk) 我们可以发现 ak=k*(sqrt(5)+1)/2 【黄金分割比】 而bk=ak+k
因此 如果(bk-ak)* (sqrt(5)+1)/2 =ak,那么就是奇异局势(必败局势)。
#include<cstdio>
#include<cmath>
using namespace std;
int main()
{
int a,b;
while(scanf("%d%d",&a,&b)!=EOF)
{
if(a>b)
{
int t=a+b;
a=t-a;
b=t-a;
}
int c=b-a;
if(a==(floor((sqrt(5.0)+1)/2*c)))
{
printf("0\n");
}
else
{
printf("1\n");
}
}
return 0;
}
4.妮姆博弈
问题描述:N堆物品,两个人轮流从某一堆中取任意多的物品。
三堆的妮姆博弈的基本奇异局势(0,0,0) (0,n,n) 对于第二个来说,如果当前者在某一堆中取了x,另一个人可以在另一堆中取x使得第一个人再次面对 (0,n,n) 。所以可以猜测,妮姆博弈的奇异局势的特点为,每堆个数抑或起来结果为0。如果不为0的话,假设前n-1堆抑或的结果为x,只要将最后一堆变为x就可以了。
结论:抑或结果为0是奇异局势,面对其者必败。
妮姆博弈-阶梯博弈
蓝桥杯 高僧斗法
时间限制:1.0s 内存限制:256.0MB
问题描述
古时丧葬活动中经常请高僧做法事。仪式结束后,有时会有“高僧斗法”的趣味节目,以舒缓压抑的气氛。
节目大略步骤为:先用粮食(一般是稻米)在地上“画”出若干级台阶(表示N级浮屠)。又有若干小和尚随机地“站”在某个台阶上。最高一级台阶必须站人,其它任意。(如图1所示)
两位参加游戏的法师分别指挥某个小和尚向上走任意多级的台阶,但会被站在高级台阶上的小和尚阻挡,不能越过。两个小和尚也不能站在同一台阶,也不能向低级台阶移动。
两法师轮流发出指令,最后所有小和尚必然会都挤在高段台阶,再也不能向上移动。轮到哪个法师指挥时无法继续移动,则游戏结束,该法师认输。
对于已知的台阶数和小和尚的分布位置,请你计算先发指令的法师该如何决策才能保证胜出。
输入格式
输入数据为一行用空格分开的N个整数,表示小和尚的位置。台阶序号从1算起,所以最后一个小和尚的位置即是台阶的总数。(N<100, 台阶总数<1000)
输出格式
输出为一行用空格分开的两个整数: A B, 表示把A位置的小和尚移动到B位置。若有多个解,输出A值较小的解,若无解则输出-1。
样例输入
1 5 9
样例输出
1 4
样例输入
1 5 8 10
样例输出
1 3
说一些似懂非懂的理解......
首先这个题目,可以把它抽象成取球模型。
想法1:最高层的人对应的堆个数为0,第二层对应的堆的个数为和上一层高度差-1(题目要求同台阶不站人)。于是我们可以把题目看成取这些堆,每次拿走任意多个,变成一个一般的尼姆博弈问题。然而( ° △ °|||)。。。我们忽略了一点,题目要求下层的人不能越过上层的人,而一般的取球模型并没有这个约束(堆之间的大小关系不能变动),如果把模型抽象成这样,对我们每次取球的个数加上了限制。( ̄ε(# ̄)
想法2:我们回想一下一般的妮姆博弈在两堆时候的情况,如果两堆大小相等,后手只需要模仿先手就可以赢得胜利,而如果两堆大小不等,先手可以将大的取成和小的一样,然后模仿后手的行为就能取胜。
在上图中,我们可以将A-B、C-D间的距离当成尼姆堆:
A.如果当前是奇异局势,先手将B上移x,那么后手可以追随将A上移x,使得先手再度面临奇异局势。如果先手将A上移动,我们也可以将C上移使得最后抑或出来结果依旧为0,依旧是奇异局势。
B.如果不是奇异局势,那么先手让后手面临奇异局势即可胜利。
总之分析问题不要太重过程,在上述分析中我们并没有深刻地讨论具体移动的步骤,我们只是抽象出了那些会影响胜负的因素罢了。
AC代码:
#include<iostream>
using namespace std;
int Arr[105],Rest[105];
int main(){
int n=1,i,j,res=0,temp,cnt;
while(cin>>Arr[n]){
n++;
}
n--;//总数
for(i=2;i<=n;i+=2){
Rest[i/2]=Arr[i]-Arr[i-1]-1;
res^=Rest[i/2];
}
if(!res){
cout<<-1;
}
else if(n<=3){
cout<<Arr[1]<<" "<<Arr[2]-1;
}
else{
for(i=1;i<n;i++){
for(j=1;Arr[i]+j<Arr[i+1];j++){//i为奇数 尼姆堆变小 否则尼姆堆变大
cnt=(i+1)/2;
res^=Rest[cnt];
temp=(i&1?Rest[cnt]-j:Rest[cnt]+j);
res^=temp;
if(!res){
cout<<Arr[i]<<" "<<Arr[i]+j;
break;
}
res^=temp;
res^=Rest[cnt];
}
}
}
return 0;
}