高僧斗法
这个题目是一个nim游戏的变种
题意是:
古时丧葬活动中经常请高僧做法事。仪式结束后,有时会有“高僧斗法”的趣味节目,以舒缓压抑的气氛。
节目大略步骤为:先用粮食(一般是稻米)在地上“画”出若干级台阶(表示N级浮屠)。又有若干小和尚随机地“站”在某个台阶上。最高一级台阶必须站人,其它任意。(如图1所示)
两位参加游戏的法师分别指挥某个小和尚向上走任意多级的台阶,但会被站在高级台阶上的小和尚阻挡,不能越过。两个小和尚也不能站在同一台阶,也不能向低级台阶移动。
两法师轮流发出指令,最后所有小和尚必然会都挤在高段台阶,再也不能向上移动。轮到哪个法师指挥时无法继续移动,则游戏结束,该法师认输。
对于已知的台阶数和小和尚的分布位置,请你计算先发指令的法师该如何决策才能保证胜出。
思路:
首先想到的是搜索,就是枚举每次可行的走法,来判断是不是必败态
但是暴力做法会超时的,然后我们可以这么抽象,把相邻的和尚距离看作一堆石子,为什么可以这么看呢?其实就是一层抽象,对于对方走的某个和尚的步数,我们可以跟随与他相邻的和尚的步数来保证当前状态的不变性,直到某一个和尚到达顶峰,这时就对这个区间抽象为某一堆石子,这时就变成了nim游戏。
过程有点抽象,但是仔细想一下确实最终变成k堆石子的nim游戏
代码:
其中dfs为搜索(超时了)
check中为nim游戏(0ms通过)
nim游戏的结论是:每一堆石子的异或和为0那么这个状态就是必败态
#include <iostream> #include <vector> #include <map> using namespace std; vector<int> st; vector<int> _end; map<vector<int>,int> mp; bool dfs(vector<int>& now){ if(now==_end)return false; if(mp[now]!=0)return mp[now]==1; for(int i=0;i<now.size()-1;i++){ int dta=now[i+1]-now[i]-1; if(dta==0)continue; for(int k=1;k<=dta;k++){ now[i]+=k; if(!dfs(now)){ now[i]-=k; mp[now]=1; return true; } now[i]-=k; } } mp[now]=-1; return false; } bool check(vector<int> s){ int ans=0; for(int i=0;i<s.size()-1;i+=2){ ans^=(s[i+1]-s[i]-1); } return ans==0; } //我们可以通过跟随下一个人的走法保持对面必败的局面,或者是移动其他堆的和尚来保持对手处于必败局面 //可以这么理解,如果是选择跟随的话相当于一回合延续了好久,就是到了最后才不得不变成一堆,然后各自取(这样就抽象为nim游戏了) int main(){ int pos; mp.clear(); st.clear(); _end.clear(); while(cin>>pos){ st.push_back(pos); } int n=st.size(); int maxpos=st.back(); int estpos=maxpos-n+1; while(estpos<=maxpos){ _end.push_back(estpos); estpos++; } if(st==_end){ cout<<-1<<" "<<-1<<endl; return 0; } // int ans=0; for(int i=0;i<n-1;i++){ int dta=st[i+1]-st[i]-1; if(dta==0)continue; for(int k=1;k<=dta;k++){ st[i]+=k; if(check(st)){ cout<<st[i]-k<<" "<<st[i]<<endl; return 0; } st[i]-=k; } } cout<<-1<<" "<<-1<<endl; return 0; }

浙公网安备 33010602011771号