[蓝桥杯 2013 国 B] 高僧斗法
题目描述
古时丧葬活动中经常请高僧做法事。仪式结束后,有时会有“高僧斗法”的趣味节目,以舒缓压抑的气氛。
节目大略步骤为:先用粮食(一般是稻米)在地上“画”出若干级台阶(表示 N N N 级浮屠)。又有若干小和尚随机地“站”在某个台阶上。最高一级台阶必须站人,其它任意。(如图 1 1 1 所示 )。
两位参加游戏的法师分别指挥某个小和尚向上走任意多级的台阶,但会被站在高级台阶上的小和尚阻挡,不能越过。两个小和尚也不能站在同一台阶,也不能向低级台阶移动。
两法师轮流发出指令,最后所有小和尚必然会都挤在高段台阶,再也不能向上移动。轮到哪个法师指挥时无法继续移动,则游戏结束,该法师认输。
对于已知的台阶数和小和尚的分布位置,请你计算先发指令的法师该如何决策才能保证胜出。
输入格式
输入数据为一行用空格分开的 N N N 个整数,表示小和尚的位置。台阶序号从 1 1 1 算起,所以最后一个小和尚的位置即是台阶的总数。( N < 100 , N<100, N<100, 台阶总数 < 1000 <1000 <1000)
输出格式
输出为一行用空格分开的两个整数 : A , B A,B A,B, 表示把 A A A 位置的小和尚移动到 B B B 位置。若有多个解,输出 A A A 值较小的解,若无解则输出 − 1 -1 −1。
样例 #1
样例输入 #1
1 5 9
样例输出 #1
1 4
样例 #2
样例输入 #2
1 5 8 10
样例输出 #2
1 3
提示
时限 1 秒, 64M。蓝桥杯 2013 年第四届国赛
思路
首先我们得知道Nim游戏:(注意上面出现的红体字,其中的第 k 位指的是如上右图的 s 0101 我们去找上面的数字谁的第3位是1,我们发现是7,那么我们就让7^s去替换7,得到的结果是 0 ,说明对于必胜态,它的后继节点至少存在一个是必败态(因为你可能不是替换7))。
然后我们延申到台阶的。
上图为台阶型。
上图圈圈的是看成每个台阶上的石子。
因此这道题就是很典型的台阶性Nim游戏,只不过它求的是方案。哎,方案,还要字典序最小,你就从小到大枚举就行了呀。然后看看每次枚举后是否符合先手必胜。
代码(附详细讲解)
//这道题就是Nim游戏(台阶型)求方案的问题
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int w[N];
int tot;
int b[N];//相邻台阶数——此时相当于每组的石子数
bool Nim(){
int res=0;
for(int i=0;i<n;i+=2){//每两个和尚组成一堆,所以每两个一组
res^=b[i];
}
// if(res==0)return true;//先手必败
// return false;//先手必胜
return res==0;
}
int main(){
while(cin>>w[n])n++;
//这边就得统计每组的石子数啦
for(int i=0;i<n-1;i++){
b[i]=w[i+1]-w[i]-1;
}
n--;//最后一个小和尚没什么用,直接删掉
if(Nim())puts("-1");
else{
//其实记录方案数就是每移动一次看是否满足条件
for(int i=0;i<n;i++){
for(int j=1;w[i]+j<w[i+1];j++){//枚举每级台阶
b[i]-=j;
if(i)b[i-1]+=j;//w[i]向上走了j级,那 b[i-1]
//则要增加 j 级,如果动的是w[1],由于他下面没有小和尚了,所以不用改;
if(Nim()){
cout<<w[i]<<' '<<w[i]+j<<endl;
break;
}
b[i]+=j;
if(i)b[i-1]-=j;
}
}
}
return 0;
}