问题描述:
设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为1,2,…,n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则1:每次只能移动1个圆盘;
规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上;
规则3:在满足移动规则1和2的前提下,可将圆盘移至a,b,c中任一塔座上。
设计思路:
需要多少次:hannio(int n)
圆盘数量只有两三个的时候大家都能想明白,就不细说了。如果我们不考虑实际步骤,即不去关心每一步到底怎么移动,只想得出具体需要的次数其实很简单。怎么考虑呢?设n个圆盘一共需要移动的次数为f(n),那怎么求解f(n)呢?仔细想想,不管具体步骤多么繁琐,当我们移动最后一个,即第n个圆盘的时候,情况一定是塔A上只剩下第n个圆盘,并且塔C上一个圆盘也没有,那么按照规则,此时剩余的n-1个圆盘全部在塔B上按照从小到大的顺序排列着。那之前的所有步骤相当于把前面的n-1层从塔A全部移动到塔B上,这和f(n-1)【即把n-1个圆盘从塔A移动到塔C上的次数】的次数显然相同;现在来看后面的步骤,当我们把第n个圆盘移动到塔C上后,此时剩余的n-1个圆盘还在塔B上,塔A上一个圆盘也没有,而塔C上只有一个最大的圆盘,后面的步骤就是把塔B上的n-1个圆盘移动到塔C上,因为塔C上最大的圆盘对移动剩余的圆盘没有影响【不会违反规则2】,此时剩余的步骤和f(n-1)【即把n-1个圆盘从塔A移动到塔C上的次数】的次数显然相同,那我们就可以得出:f(n)=f(n-1)+1 + f(n-1),即f(n)=2f(n-1)+1
核心代码:
int hannio(int n)
{
if (n == 1)
return 1;
else
return 2*hannio(n - 1)+1;
}
显然,直接用递归算法就可以得出所需的次数,那我们想知道具体步骤呢?
具体步骤:Hannio(int n, char a, char b, char c)
上面我们已经明白了具体的步骤划分,那么现在只需要加上具体的移动方向,不妨设解决n个圆盘的问题的具体步骤为Hannio,此时我们除了传进去n外,还要把ABC三个塔传进去,从哪个到哪个,(int n, char a, char b, char c)我们定义它为从A到C,中途借助了B;首先要判断有没有到达边界条件,即n=1,如果没有,再执行前半部分,即把前面的n-1层从塔A全部移动到塔B上,然后移动最后第n个圆盘,即Move(n, a, c),之后再执行后半部分,把塔B上的n-1个圆盘移动到塔C上,至此,我们便完成了整个递归过程。
核心代码:
void Move(int n, char a, char b)//从A移动到B
{
cout << "把第" << n << "个圆盘从" << a << "移动到" << b << endl;
}
void Hannio(int n, char a, char b, char c)
{
if (n == 1)
{
Move(n, a, c);//最后一步,把第一个圆盘从A移动到C
}
else
{
Hannio(n - 1, a, c, b);//把前面的n-1层从塔A全部移动到塔B上
Move(n, a, c);//然后移动最后第n个圆盘,从A到C
Hannio(n - 1, b, a, c);//把塔B上的n-1个圆盘移动到塔C上
}
}
总结:
递归算法就是把问题大而化之,就像本题,我们把步骤分为前n-1个圆盘从A到B,再移动第n个圆盘从A到C,最后把剩余的n-1个圆盘从B到C移动,这样就可以一步步递归,从而把问题分解。当然,递归里面非常重要的前提是弄清楚边界,这道题里的边界就是最后n一步步减小最后到1时,只有一个圆盘的时候,只用移动一位,这个时候递归就结束了。