首页 > 技术文章 > 【题解】2019,4.24模拟赛(汉诺塔)

JCNL666 2019-04-24 22:31 原文

\(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;
}

推荐阅读