汉诺塔问题

【题目描述】

约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到中间的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。

这是一个著名的问题,几乎所有的教材上都有这个问题。由于条件是一次只能移动一个盘,且不允许大盘放在小盘上面,所以64个盘的移动次数是:18,446,744,073,709,551,615

这是一个天文数字,若每一微秒可能计算(并不输出)一次移动,那么也需要几乎一百万年。我们仅能找出问题的解决方法并解决较小N值时的汉诺塔,但很难用计算机解决64层的汉诺塔。

假定圆盘从小到大编号为1, 2, ...

 

【输入】

输入为一个整数(小于20)后面跟三个单字符字符串。

整数为盘子的数目,后三个字符表示三个杆子的编号。

 

【输出】

输出每一步移动盘子的记录。一次移动一行。

每次移动的记录为例如 a->3->b 的形式,即把编号为3的盘子从a杆移至b杆。

 

【输入样例】

2 a b c

【输出样例】

a->1->c
a->2->b
c->1->b

例题不怎么详的解:
其实这一题搞半天了还是没什么吃透。总感觉哪里怪怪的。但是如果硬要说,这题无论是思路还是代码实现都很简单。

但是总感觉有点。。。我居然还去7k7k玩汉诺塔小游戏。。。怪难理解的这递归。
几乎所有解析都是一个思路,例子也是一样的。64个盘子,拿63个当整体的都看到无数遍了,但是总感觉这样解释有点。。。
据我分析,无论多少个盘子,实际上都分为三个步骤(设共有n个盘子,a是起始柱,b是中转柱,c是目标柱):

1、将n-1个盘子移至b柱;
2、将第n个盘子移到c柱(需要一步);
3、将n-1个盘子移到第n个盘子上面;
第一步又可以分解为依次将一个盘子,两个盘子,三个盘子,四个···移动到b柱上,巧的是,移动x个盘子到b柱上所需步数恰好是仅有x个盘子时达到目标所需步骤。
第二步多出来的那一步就是将第n个盘子挪到c那一步;
第三步和第一步一样,把n-1个盘子从b挪到c,也就是第n个盘子上;

显而易见,若当前盘子为x个,那么所需步骤为(移动x-1个盘子所需步骤)*2+1步(这里是为了递归好理解,当然也可以理解做2^n-1);

当然,这只是基本规律,在不清楚算法之前,首先理解了这一点,相当于把握了这道题的关键,接下来的递归操作,得到每一步的走法,其实不过是将基础的步骤重复几遍。

废话不多说,暴力的规律在这里:
1——1
2——3
3——7
4——15
5——31
6——63
7——127
我一看,规律明摆着在这里嘛!

好的,有句老话说得好,无图说个***,接下来,上图(以四个盘子为例)!

第一步:



我虽然有些顾虑,就是这一坨满满变高的塔每增高一格就会跑到另一根柱子上去。
但是事实证明,最后的递归算法很“聪明”,巧妙转换了每一次塔的位置,将塔在b,c两柱之间对调。

第二步:

看到没,把第n个盘子从a移到b,走了一步;

第三步:

其实是重复第一步!!!

 

到最后完成!

到这里,我希望也相信大家包括我自己都可以对解汉诺塔的原理有个清晰完整的了解,而不是像别的解析一样上来就讲算法。

算法部分:

重头戏来了。

这个算法就比较神奇了。

具体怎么神奇呢?——本算法没有任何需要储存的东西,除了调用栈,这货就是把移动方式给你打印一遍,实际上自己也不知道自己在干什么。

所以!很多初学者,包括我,也在这里绕晕了。

慢慢来,先是第一步,从2个盘子看起,显而易见,最快的方法是将最上面的第1个盘子放在中转柱上,接下来将第2个盘子放在目标柱上,最后将第1个盘子放到目标柱上,共三步。别小看了这三步!这可是历史性的最基础的三步!为啥?前面讲到,若当前盘子为x个,那么所需步骤为(移动x-1个盘子所需步骤)*2+1步;

恍然大悟!得出以(n-1)作为递归式,(n=0或1)作为递归边界。。。好吧这很明显。(虽然两个递归边界得出的是两种不同的算法)

重点在下面的移动上,这里肯定倒了一片人,当然还有我。

 

 

牢记!!!

先将最上面的盘子从起始柱a柱移到中转柱,也就是b柱上。即 a->1->b;

接下来,将第2个盘子放在目标柱上。即 a->2->c;

最后,将第1个盘子放到c柱上。 即 b->1->c;

完成!这样就明白多了!(其实我写到这里时还是一脸懵逼。。。)

由此得出递归式,Hanoi(n-1,a,b,c),输出a->n->b,表示把a上第n个盘子移到b上,将c柱作为中转柱;Hanoi(n-1,b,c,a),表示把b柱上的第n个盘子移到c柱上,将a柱作为中转柱。

很疑惑啊对不对!中间的第二步呢!?其实,它“藏”在第三步里,我们在写程序时,将第三步写在最后,就达到了效果。

void mov(int n,char a,char c,char b)
{
	if(n==0) return;
	mov(n-1,a,b,c);
	cout<<a<<"-->"<<n<<"-->"<<c<<endl;
	mov(n-1,b,c,a);
}

  

 

 好吧说实话我也不是很懂。。。

 

样例代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
char x,y,z;
void mov(int n,char a,char c,char b)
{
	if(n==0) return;
	mov(n-1,a,b,c);
	cout<<a<<"-->"<<n<<"-->"<<c<<endl;
	mov(n-1,b,c,a);
}
int main()
{
	cin>>n;
	cin>>x>>y>>z;
	mov(n,x,y,z);
	return 0;
}

  

看起来很简单把。

2019-01-30 22:52:48

 

 

 

转载于:https://www.cnblogs.com/DarkValkyrie/p/10340141.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值