汉诺塔问题深剖|汉诺塔问题变形|函数递归

一. 汉诺塔问题

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;
}

三. 总结:

诸如汉诺塔问题,此等复杂的、多过程的实际问题,可尝试有简单的情形入手寻找规律,常怀大事化小的思路,试着去推导递推关系,用递归的思路去简化问题。

若有递推关系式,则不要止步于此,想想还有没有更本质的结论,从而,能简化优化代码的复杂程度,并提高代码效率。

此外,我一直都觉得,递推的思想有着一种难以言喻的奇妙的逻辑美,多见识多感受,真的会越来越喜欢计算机语言和计算机思维,既严谨周全,又妙不可言!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值