一. 汉诺塔问题
1. 概述
2. 题目
3. 思路分析
4. 代码实现
二. 汉诺塔问题变形
1. 题目
2. 思路分析
3. 代码实现
三. 总结
一. 汉诺塔问题:
1. 概述:
汉诺塔(Hanoi)问题是一个著名的问题。十九世纪末,欧洲的商店中出售一种智力玩具,在一块铜板上有3根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔,游戏的目的是将最左边A杆上的圆盘,借助最右边的C杆,全部移到中间的B杆上,条件是一次仅能移动一个盘,且不允许大盘放在小盘的上面。相传古印度布拉玛神庙中有一个僧人,他每天不分白天黑夜,不停地移动那些圆盘,据说,按照以上规则,当所有64个圆盘全部从一根杆上移到另一杆上的那一天就是世界的末日。过汉诺塔问题又被称为“世界末日问题”。
而移动64个盘子需要的次数是:18,446,744,073,709,551,615,这是一个天文数字若每一微秒可能计算一次移动,那也需要几乎一百万年。
所以以下,只能给出分析汉诺塔问题的方法,而所数字过大,不能实际解决这个问题。
2. 题目:
有三根杆(设为 A、B、C),在A杆上有n个大小均不相同的盘子,大的在下,小的在上。
要求:
按以下规则,把这n个盘子从A杆搬到B杆上:
①在搬动过程中可使用C杆;
②每次只允许搬动一个盘子;
③在搬动过程中,必须保证大盘子在下,小盘子在上。
3. 思路分析:
由上规律可得出一种递归思想:即,将1 - n-1号盘视作一个整体,借一杆移到另一杆上,需F(n-1),即前一次的步数。
所以对于n个盘子的情况:
总体上分为3步:
1.将1 - n-1号盘视作一个整体,借B杆移到C杆上;(需F(n-1)步)
2.将n号盘移到B杆上;(需1步)
3.将1 - n-1号盘这个整体,借A杆移到B杆上;(需F(n-1)步)
所以,满足这一递推关系:F(n)=2*F(n-1)+1。
进一步利用数学知识推导,可得出更本质的结论:F(n)=2^n-1.(推导如下)
由此,程序得到了极大的简化优化!
4. 代码实现:
#include <stdio.h>
#include <math.h>//pow函数的头文件;
int main()
{
long long n, Move;
scanf("%lld", &n);
Move = pow(2, n) - 1;//该过程也可用循环实现,其本质为迭代;
printf("%lld\n", Move);
return 0;
}
二. 汉诺塔问题变形
1. 题目:
**BC161 大吉大利,今晚吃鸡 **
描述
糖和抖m在玩个游戏,规定谁输了就要请谁吃顿大餐:抖m给糖a b c三个驻, 并在a柱上放置了数量为n的圆盘,圆盘的大小从上到下依次增大,现在要做的事就是把a柱的圆盘全部移到c柱,移动的过程中保持小盘在上,大盘在下,且限定圆盘只能够移动到相邻的柱子,即a柱子上的圆盘只能够移动到b,b柱子上的圆盘只能够移动到a或者c,c同理。现在请你设计一个程序,计算所需移动的最小步数, 帮助糖赢得大餐!
输入描述:
每一行输出有一个整数n(0<=n<26), 直至文件末尾。
输出描述:
对于每一组数据,输出一行,输出移动的最小步数M。
示例1
输入:1
输出:2
2. 思路分析:
与经典的汉诺塔问题最大的不同在于:
限定只能移到相邻的杆上,所以好处是相对来说每一步的走法比较固定,但坏处是重复的动作多了很多,容易绕晕,更需要我们找到规律,总结出抽象的递归思想。
由上规律可得出一种递归思想:即,将1 - n-1号盘视作一个整体,先移到相邻杆、再移到另一杆上,需F(n-1),即前一次的步数。
所以对于n个盘子的情况:
总体上分为5步:
1.将1 - n-1号盘视作一个整体,先移到相邻的b杆,再移到c杆上;(需F(n-1)步)
2.将n号盘移到b杆上;(需1步)
3.将1 - n-1号盘这个整体,先移到相邻的b杆,再移到a杆上;(需F(n-1)步)
4.将n号盘移到c杆上;(需1步)
5.将1 - n-1号盘这个整体,先移到相邻的b杆,再移到c杆上;(需F(n-1)步)
所以,满足这一递推关系:F(n)=3*F(n-1)+2。
进一步利用数学知识推导,可得出更本质的结论:F(n)=3^n-1.(推导过程同上不再重复,读者可自行尝试,欢迎与我讨论~)
3. 代码实现:
#include <stdio.h>
#include <math.h>
//类汉诺塔问题;
//只是因为只能移动到相邻杆上,所以步骤略不同,
//但是分为n-1和第n号盘的思想是相同的;
//公式F(n)=3*F(n-1)+2;
//进一步得出等比公式:F(n)=3^n-1;
int main()
{
long long n, M;
while (scanf("%lld", &n) != EOF)
{
M = pow(3, n) - 1;
printf("%lld\n", M);
}
return 0;
}
三. 总结:
诸如汉诺塔问题,此等复杂的、多过程的实际问题,可尝试有简单的情形入手寻找规律,常怀大事化小的思路,试着去推导递推关系,用递归的思路去简化问题。
若有递推关系式,则不要止步于此,想想还有没有更本质的结论,从而,能简化优化代码的复杂程度,并提高代码效率。
此外,我一直都觉得,递推的思想有着一种难以言喻的奇妙的逻辑美,多见识多感受,真的会越来越喜欢计算机语言和计算机思维,既严谨周全,又妙不可言!!!