【题解】2019,4.24模拟赛(汉诺塔)
\(Description:\)
给出汉诺塔玩了几步之后的状态,\(pos[i]\) 表示第 \(i\) 小的盘子在 \(pos[i]\)。 求出玩到现在是不是最优解,如果是最优解,那么现在是第几步。
\(Sample\) \(Input:\)
2
3 1
\(Sample\) \(Output:\)
1
\(Solution:\)
害怕,我居然从来没做过汉诺塔的题。。。
不会做汉诺塔的我瑟瑟发抖。
其实这题会做汉诺塔的人大概都会,就是考虑:
首先先把当前状态还原回初始柱。
然后在按最优解把盘子移到第二根。
两者步数之和一加,看看和最优解是否次数相同。
汉诺塔 \(n\)个盘子的最优解移动次数是 \(2^{n}-1\)。
那么先来看一下普通的汉诺塔代码。
inline int Get_next(int now,int to){
if(now==1) return (!(to==2));
if(2==to) return Get_next(now-1,to);
int useful=1;// 找到下一根可用的中转柱子
while(useful==pos[now] || useful==to) useful++;
return ((1<<(now-1))+Get_next(now-1,useful));
// 把上面一堆放到中转柱上,最优方法为1<<(now-1)-1,
// 因为还要加上当前这个圈移到目标柱上
}
倒做回去的代码其实也差不多:
inline int Get_pre(int now,int from,int to){
// from =>现在在的柱子
// to =>要去的柱子
if(now==1) return (!(from==to));
if(from==to) return Get_pre(now-1,to,pos[now-1]);
int useful=1;
while(useful==from || useful==to) useful++;
return ((1<<(now-1))+Get_pre(now-1,useful,pos[now-1]));
}
完整代码:
#include<cstdio>
#include<iostream>
#define int long long
using std::scanf;
using std::printf;
const int N=32;
int n,part1_ans,part2_ans;
int pos[N];
inline int Get_pre(int now,int from,int to){
if(now==1) return (!(from==to));
if(from==to) return Get_pre(now-1,to,pos[now-1]);
int useful=1;
while(useful==from || useful==to) useful++;
return ((1<<(now-1))+Get_pre(now-1,useful,pos[now-1]));
}
inline int Get_next(int now,int to){
if(now==1) return (!(to==pos[now]));
if(pos[now]==to) return Get_next(now-1,to);
int useful=1;
while(useful==pos[now] || useful==to) useful++;
return ((1<<(now-1))+Get_next(now-1,useful));
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i) scanf("%lld",&pos[i]);
part1_ans=Get_pre(n,1,pos[n]);
part2_ans=Get_next(n,2);
if(part1_ans+part2_ans>(1<<n)-1) return puts("-1"),0;
printf("%lld\n",part1_ans);
return 0;
}