题为c程序设计(第五版) 谭浩强 重点例题
前言
问题来源:
汉诺塔(也称河内塔)是有法国数学家爱德华·卢卡斯于1883年发明的一道智力题。它源于印度的一个古老传说:大梵天创造世界的时候做了三根钻石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令一组牧师把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。据说牧师们夜以继日地工作,当他们完成任务时,那座塔就将坍塌,世界也将毁灭。
汉诺塔问题是这样规定的:一个由64个圆盘组成的塔,这些圆盘从下向上按照大小递减的方式套在一根柱子上,我们的目的是要将这根柱子上的所有圆盘移动到另一根柱子上,每次只能移动一个圆盘,且在移动过程中较大的圆盘不能放置在较小的圆盘上面。图示如下:
基本思路如下:
1.将A柱上的n-1个盘子借助C柱移向B柱
2.将A柱上仅剩的最后一个盘子移向C柱
3.将B柱上的n-1个盘子借助A柱移向C柱
一、问题分析
牧师会这么想:假如有另外一个牧师能有办法将上面63个盘子从一个座移到另一座,那么问题就解决了。此时牧师只需那么做:
1.命令第二个牧师将63个盘子从A座移到B座;
2.自己将1个盘子(最底下的、最大的盘子)从A座移到C座;
3.再命令第2个和尚将63个盘子从B座移动到C座。
图解如下(以三个盘子为例):
至此,全部任务完成了。这就是递归方法,把移动64个盘子简化为移动63个盘子,难度减小了一些。但是,有一个问题实际上未解决:第2个牧师怎样才能将63个盘子从A座移动到B座?
为了解决将63个盘子从A座移动到B座,第2个牧师又想:如果有人能将62个盘子从一个座移动到另一个座,我就能将63个盘子从A座移动到B座,他是这样做的:
1.命令第3个牧师将62个盘子从A座移动到C座;
2.自己将1个盘子从A座移到B座;
3.再命令第3个牧师将62个盘子从C座移到B座。
再进行一次递归。如此“层层下放”,直到后来找到第63个牧师,让他完成将两个盘子从一个座移动另一个座,进行到此,问题就接近解决了。最好找到第64个牧师,让他完成将一个盘子移到另一个座,至此,全部工作都已落实,是可以执行的。
可以看出,递归的结束条件是最后一个牧师只须移一个盘子,否则递归还要继续进行下去。
应当说明,只有第64个牧师的任务完成后,第63个牧师的任务才能完成。只有第2~64个和尚的任务都完成后,第一个和尚的任务才能完成。这是一个典型的递归的问题。
为便于理解,先分析将A座上3个盘子移到C座上的过程:
1.将A座上的2个盘子移到B座上(借助C座)。
2.将A座上1个盘子移到C座上。
3.将B座上2个盘子移到C座上(借助C座)。
其中第2步可以直接实现。第1步又可用递归方法分解为:
·将A座上1个盘子从A座移到C座;
·将A座上1个盘子从A座移到B座;
·将C座上1个盘子从C座移到B座;
第3步可以分解为
·将B座上1个盘子从B座移到A座上;
·将B座上1个盘子从B座移到C座上;
·将A座上1个盘子从A座移到C座上;
将以上综合起来,可得到移动3个盘子的步骤为
A->C,A->B,C->B,A->C,B->A,B->C,A->C
共经历7步。由此可以推出:移动n个盘子要经历(2**n-1)步。
由上面的分析可知:将n个盘子从A座移到C座可以分解为一下3个步骤:
1.将A座上n-1个盘借助C座先移到B座上;
2.把A座上剩下的一个盘移到C座上;
3.将n-1个盘从B座借助于A座移到C座上。
上面第1步和第3步,都是把n-1个盘从一个座移到另一个座上,采取的方法是一样的,只是座的名字不一样而已。为使之一般化,可以将第1步和第3步表示为:
将one座上n-1个盘移到two座(借助three座)。只是在第1步和第3步中,one,two,three和A,B,C的对应关系不同。对第1步,对应的关系是one对应A,two对应B,three对应C。对第3步,是one对应B,two对应C,three对应A。
因此,可以将上面3个步骤分为两类操作:
1.将n-1个盘从一个座上移到另一个座上(n>1)。这就是大牧师让小牧师座的工作,它是一个递归的过程,即牧师将任务层层下放,直到第64个牧师为止。
2.将一个盘子从一个座移到另一个座上。这是大牧师做的工作。
二、实现过程
1.具体代码(示例)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
void hanoi(int n, char one, char two, char three);
int m;
printf("input the number of disks:");
scanf("%d", &m);
printf("The step to move %d disks:\n", m);
hanoi(m, 'A', 'B', 'C');
return 0;
}
void hanoi(int n, char one, char two, char three)
//将n个盘从one座借助two座,移到three座
{
void move(char x, char y);
if (n == 1)
move(one, three);
else
{
hanoi(n - 1, one, three, two);
move(one, three);
hanoi(n - 1, two, one, three);
}
}
void move(char x, char y)
{
printf("%c-->%c\n", x, y);
}
2.运行结果
总结
汉诺塔是一个典型的递归问题,可能比较晦涩难懂,建议各位友友可以反复观看(以三个盘子为例),我也是看了好几遍才能懂得其中的奥妙,慢慢来啦,头疼,哈哈哈哈哈哈哈。
如果有什么问题,请各位指正!!!