一.问题来源
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号X、Y、Z),在X杆自下而上、由大到小按顺序放置64个金盘。游戏的目标:把X杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于X、Y、Z任一杆上。
二.小游戏体验
以三个为例,移动方法如下(小游戏网址:汉诺塔小游戏)
三.思路分析
以最简单的例子分析,当我们想把3个金盘从X杆移动到Z杆上时,可以想象,先通过Z,将除了最大金盘以外的所有金盘移动到Y杆上,再将最大(最底下)的金盘移动到Z杆上,再通过X杆,将Y杆上的所有金盘移动到Z杆上:
即
- 通过Z杆,将2个金盘移动到Y杆:
- 将最大的金盘移动到Z上:
- 借助X,将Y杆上的金盘移动到Z上:
所以,当有4个金盘时,可以将前3个金盘先移动到Y,再将最大的金盘移动到Z,再将Y上金盘移动到Z,而这3个金盘的移动可以依照前面的分析,而5个金盘则可以看成先移动前4个,以此类推,这便是递归的思想
也就是说,当有64个金盘时,我们其实要解决两个问题:
- 将X上的63个金盘通过Z移动到Y
- 将Y上的63个金盘通过X移动到Z
这时,将问题1问题2分别处理:
问题1将X上的63个金盘通过Z移动到Y分解为:
- 先将前62个盘子移动到Z上,确保大盘在下,小盘在上
- 再将最底下的第63个盘子移动到Y上
- 最后将Z上的62个盘子移动到Y上
问题2将Y上的63个金盘通过X移动到Z分解为:
- 先将62个盘子移动到X上
- 再将最底下的第63个盘子移动到Z上
- 最后将X上的62个盘子移动到Y上
依次递归便可以完成任务。
四. 程序实现
#include <stdio.h>
#include <getopt.h>
void move(int n,char x,char y,char z) //将n个盘子从x借助y移动到z
{
if(1 == n)
{
printf("%c-->%c\n",x,z);
}
else
{
move(n-1,x,z,y); //将n-1个盘子从x借助z移动到y
printf("%c-->%c\n",x,z); //将第n个盘子从x移动到z
move(n-1,y,x,z); //将n-1个盘子从y借助x移动到z
}
}
int main(int argc,char **argv)
{
int ch;
int num;
struct option opts[] = {
{"number",required_argument,NULL,'n'},
{NULL,0,NULL,0}
};
while((ch = getopt_long(argc,argv,"n:",opts,NULL)) != -1) //通过命令行传参指定金盘个数
{
switch(ch)
{
case 'n':
num = atoi(optarg);
break;
default:
break;
}
}
printf("移动步骤如下:\n");
move(num,'X','Y','Z');
return 0;
}
运行程序:
通过 -n 指定金盘数量,当有3个金盘时,操作如图,对比前面的动图,操作正确。
这便是经典的汉诺塔问题,在理解这个问题的解决方法上花了很大的功夫,然而代码实现却如此简单,这也许就是算法与程序结合所带来的魅力吧!
古印度僧侣曾预言,当圣庙中的金盘全部从3根针中的一根全部(64)移动到另外两根中的一根时,世界将会迎来灭亡…