C++求解多层(多阶)汉诺塔问题(数学解法)

先上题目:

【问题描述】
相信大家应该做过最经典的汉诺塔题目,那什么是多层汉诺塔呢?
【题目描述】 汉诺塔是一个有意思的游戏,每个柱子上套上多个中心有洞的圆盘,每次只能移动一个圆盘,并且每个圆盘不能放在比它面积小的圆盘的上面。现在有三套圆盘并叠加放在一个柱子上了,请移动圆盘,使每个柱子上的圆盘都按照相同的顺序从大到小的摆放好,也就是把三份盘子平均分开。请问对于 n 个不同数量的圆盘(也就是共有 3*n 个盘子),分别在每个柱子上分好 n 个盘子,最少需 要移动多少步?示意图如下图。

【输入格式】 输入共 1 行,包括一个正整数 n

【输出格式】输出共 1 行,一个整数,表示需要移动圆盘的最少的步骤数。

【样例输入】 1

【样例输出】 2

本文概念说明:多层汉诺塔即多阶汉诺塔(阶数为1或3的整倍数)

汉诺塔例举:1、单层汉诺塔:以一个5个圆盘的汉诺塔为例,则这个汉诺塔有5个大小不一样的圆盘。2、以一个有9个圆盘的三阶汉诺塔为例,则这个汉诺塔的圆盘分布为从下到上、从大到小的三层,每层具有三个大小一样的圆盘。3、n阶汉诺塔以此类推(n为3的整倍数)。

      数学解决复杂问题的常见思路之一是:先考虑其简单的情景,具体而言,对于多层汉诺塔问题,我们可以先看简单的情形:单层汉诺塔问题。

单层(一阶)汉诺塔(数学解法)       

  

解法一:归纳法(低年级)

        我们先统计前面几种比较简单的情况的步骤数:        

        通过观察上表中步数和层数的规律,我们不难发现其满足关系式f(n)=2^n-1

      解法二:递推法(高年级)

先看下面动画:  

      

        通过观察上图中的动画可知:我们的目标是将n个圆盘从A柱移动到C柱上。

        移动思路:1、先将上面的n-1个圆盘从A柱移动到B柱;2、将最下方的一个圆盘从A柱移动到C柱;3、再将B柱上的n-1个圆盘移动到C柱上。(在递归求解的思路中这里是递归的部分,边界为层数为1时。)

        通过上面的分析可以得出实现f(n)的过程如下:

        f(n)=f(n-1)+1+f(n-1)

1、我们将上面的关系式进行推导:

f(n)=2f(n-1)+1

f(n)+1=2f(n-1)+2

f(n)+1=2(f(n-1)+1)

2、到这里我们可以得出:

\frac{f(n)+1}{f(n-1)+1}=2

\frac{f(n-1)+1}{f(n-2)+1}=2

......

\frac{f(2)+1}{f(1)+1}=2

进行累乘得到\frac{f(n)+1}{f(1)+1}=2^{n-1}

3、已知只有一个圆盘时我们只需要一步(直接从A柱移动到C柱),即f(1)=1,将其带入可得出:

f(n)=2\times 2^{n-1}-1=2^{n}-1

        如果我们知道步骤的直接计算公式,那么可以直接使用cmath库的幂函数将其算出即可。具体代码如下:

#include <iostream>
#include <cmath>
using namespace std;
int main()
{
	int n;
	cin >> n;
	cout << pow(2, n) - 1;
	return 0;
}

        接下来我们回到多层(多阶)汉诺塔问题上。

多层(多阶)汉诺塔(数学解法)

先看下图中的动画:        ​​​​​

         通过观察上图中的动画,得出我们要移动n层汉诺塔的过程应该是:

        p(n)=p(n-1)+1+p(n-1)+1

        这里的p(n)记录的是将最下面一层的三个圆盘均分到三个柱子上,而这里的p(n-1)是将上面的n-1层从一根柱子移到另外一根柱子上,所以这里我们可以参考一阶汉诺塔:

        f(n)=2^{n}-1

        f(n-1)=2^{n-1}-1

        题目中求解的是三层(三阶)汉诺塔!

        所以这里的p(n-1)=3\times f(n-1)=3\times(2^{n-1}-1),将其带入到p(n)中可以得到如下结果:

        p(n)=3\times (2^{n-1}-1)+1+3\times (2^{n-1}-1)+1

        化简后得到p(n)=6\times2^{n-1}-4,到这里实际上只是计算了将第1层的3个圆盘均分到3个柱子上的步骤数,我们还需要将上边的n-1层全部求解出来,如果在这里我们避开繁琐的数学求和,那么可以通过迭代求和直接求解,代码如下:

#include <iostream>
#include<cmath>
using namespace std;
int main()
{
	int n,sum=0;
	cin>>n;
	for(int i=n;i>=1;i--)
	{
		sum=sum+6*pow(2,i-1)-4;
	}
	cout<<sum;
	return 0;
}

        如果我们需要通过数学方法完成对全过程的求解,则需要对通项为p(n)=6\times 2^{n-1}-4的数列求和。详细过程如下:

        S(n)=p(n)+p(n-1)+p(n-2)+...+p(2)+p(1)

        S(n)=6\times (2^{n-1}+2^{n-2}+2^{n-3}+...+2^2+2^1+2^0)-4\times n

        令A=2^{n-1}+2^{n-2}+2^{n-3}+...+2^2+2^1+2^0,将等式两边同时乘以2可得:

        2\times A=2^{n}+2^{n-1}+2^{n-2}+...+2^3+2^2+2^1,然后通过2\times A-A可得到:

        A=2^n-2^0=2^n-1

        在这里将A带入到S(n)中可得:

        S(n)=6\times(2^n-1)-4\times n=6\times 2^n-4\times n-6

        最后,通过代码实现求解全过程步骤数的代码如下:

	#include <iostream>
	#include<cmath>
	using namespace std;
	int main()
	{
		int n;
		cin>>n;
		cout<<6*pow(2,n)-4*n-6;
		return 0;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值