先上题目:
【问题描述】
相信大家应该做过最经典的汉诺塔题目,那什么是多层汉诺塔呢?
【题目描述】 汉诺塔是一个有意思的游戏,每个柱子上套上多个中心有洞的圆盘,每次只能移动一个圆盘,并且每个圆盘不能放在比它面积小的圆盘的上面。现在有三套圆盘并叠加放在一个柱子上了,请移动圆盘,使每个柱子上的圆盘都按照相同的顺序从大到小的摆放好,也就是把三份盘子平均分开。请问对于 n 个不同数量的圆盘(也就是共有 3*n 个盘子),分别在每个柱子上分好 n 个盘子,最少需 要移动多少步?示意图如下图。【输入格式】 输入共 1 行,包括一个正整数 n
【输出格式】输出共 1 行,一个整数,表示需要移动圆盘的最少的步骤数。
【样例输入】 1
【样例输出】 2
本文概念说明:多层汉诺塔即多阶汉诺塔(阶数为1或3的整倍数)
汉诺塔例举:1、单层汉诺塔:以一个5个圆盘的汉诺塔为例,则这个汉诺塔有5个大小不一样的圆盘。2、以一个有9个圆盘的三阶汉诺塔为例,则这个汉诺塔的圆盘分布为从下到上、从大到小的三层,每层具有三个大小一样的圆盘。3、n阶汉诺塔以此类推(n为3的整倍数)。
数学解决复杂问题的常见思路之一是:先考虑其简单的情景,具体而言,对于多层汉诺塔问题,我们可以先看简单的情形:单层汉诺塔问题。
单层(一阶)汉诺塔(数学解法)
解法一:归纳法(低年级)
我们先统计前面几种比较简单的情况的步骤数:
通过观察上表中步数和层数的规律,我们不难发现其满足关系式。
解法二:递推法(高年级)
先看下面动画:
通过观察上图中的动画可知:我们的目标是将n个圆盘从A柱移动到C柱上。
移动思路:1、先将上面的n-1个圆盘从A柱移动到B柱;2、将最下方的一个圆盘从A柱移动到C柱;3、再将B柱上的n-1个圆盘移动到C柱上。(在递归求解的思路中这里是递归的部分,边界为层数为1时。)
通过上面的分析可以得出实现f(n)的过程如下:
1、我们将上面的关系式进行推导:
2、到这里我们可以得出:
......
进行累乘得到
3、已知只有一个圆盘时我们只需要一步(直接从A柱移动到C柱),即,将其带入可得出:
如果我们知道步骤的直接计算公式,那么可以直接使用cmath库的幂函数将其算出即可。具体代码如下:
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int n;
cin >> n;
cout << pow(2, n) - 1;
return 0;
}
接下来我们回到多层(多阶)汉诺塔问题上。
多层(多阶)汉诺塔(数学解法)
先看下图中的动画:
通过观察上图中的动画,得出我们要移动n层汉诺塔的过程应该是:
这里的记录的是将最下面一层的三个圆盘均分到三个柱子上,而这里的是将上面的n-1层从一根柱子移到另外一根柱子上,所以这里我们可以参考一阶汉诺塔:
题目中求解的是三层(三阶)汉诺塔!
所以这里的,将其带入到中可以得到如下结果:
化简后得到,到这里实际上只是计算了将第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;
}
如果我们需要通过数学方法完成对全过程的求解,则需要对通项为的数列求和。详细过程如下:
令,将等式两边同时乘以2可得:
,然后通过可得到:
在这里将带入到中可得:
最后,通过代码实现求解全过程步骤数的代码如下:
#include <iostream>
#include<cmath>
using namespace std;
int main()
{
int n;
cin>>n;
cout<<6*pow(2,n)-4*n-6;
return 0;
}