汉诺塔的相关题目及计算
汉诺塔的相关介绍及分析
简介
前两天无意中提到了汉诺塔就重新刷了一下相关的题目,感觉有些题目有很多巧妙之处,提到汉诺塔首先想到的是:它是一个递归的经典例题或者是算法入门的例题,其实汉诺塔里更多的是数学思维。
经典汉诺塔
经典汉诺塔规则: 将A柱子上的圆盘移到C柱子,并且在任何情况下小盘子不能放到大盘子下面。
递归算法求汉诺塔移动方案:
介绍: 要想从某柱子移动一个盘子,首先需要将该盘子上方的所有盘子移动到其他柱子上。设A柱子上盘子数目为n,当n为1时,自然只需要移动一步,即直接将盘中从A柱子移动到C柱子即可;当n不为1时,要将A柱子上的n个盘子移动到C盘子上,首先应该将A柱子中前n - 1个盘子移动到B柱子(借助C柱子)上,再将第n个盘子从A柱子移动到C柱子,最后将前n - 1个盘中移动到C柱子(借助A柱子)即可。
代码实现:
#include<iostream>
using namespace std;
void move(char A, char C){
//输出移动过程(从A移动到C)
cout<<A<<"->"<<C<<endl;
}
void Hanoi(int n, char A, char B, char C)
if(n == 1){
//当只有一个盘子时直接将将该盘子移动到C柱子
move(A, C);
}else{
Hanoi(n - 1, A, C, B);
//先将前n - 1个盘子借助C柱子,从A柱子移动到B柱子上
move(A, C);
//再将第n个盘子从A柱子移动到B柱子上
Hanoi(n - 1, B, A, C);
//最后将B柱子上的n - 1 个盘子移动到C柱子上
}
}
int main(){
int n;
char A, B, C;
cin>>n>>A>>B>>C;//输入盘子数目和各个柱子的编号
Hanoi(n, A, B, C);
}
优点: 能够直观的观察到每一步的移动情况
缺点: 由于要进行多次递归,所以在盘子数目较大时空间压力很大。并且在多次查找的情况下,每次输入都需要进行递归,时间上也不太可观。
求最小移动次数
求最小移动次数常见的是两种问题——求n个盘子全部从A柱子移动到C柱子的最小移动次数、求n个盘子中第k个盘子移动的次数。
求n个盘子全部从A柱子移动到柱子的最小移动次数:
求移动次数的问题用递归是很不明智的,因为他的移动次数是有规律可循的——在只有一个盘子的情况下移动次数为1;两个盘子时要先将前1个盘子移到B柱子,再将第二个盘子移到C柱子;三个盘子时先将前两个盘子移到B柱子,再将第三个盘子移动到C柱子,最后将前两个盘子移动到C柱子上;以此类推,当盘子数为n时,要先将前n-1个盘子移动到B柱子上,再将第n个盘子移动到C柱子上,最后将前n-1个盘子移动到C柱子上。因为在每个过程中,第n个盘子都只移动了一次,而前n-1个盘子形成的整体移动了两次,相当于将n-1个盘子移动到目标位置的次数乘以二,由此得到递推公式:f(1) = 1, f(n) = 2 * f(n - 1) + 1;
求n个盘子中第k个盘子移动的次数:
很显然第n个盘子移动的次数是1次,前n - 1个盘子要先移动到B柱子再移动到C柱子,所以第n-1个盘子要移动2次,并且在这个过程中第n-2个盘子要移动4次……,以此类推第k个盘子的移动次数是2^(n - k)。
例题:
AC代码:
#include<iostream>
#include<cmath>
using namespace std;
int main(){
int t, n, k;
cin >> k;
while(t--){
cin >> n >> k;
__int64 ans = pow(2, n - k);
cout<<ans<<endl;
}
return 0;
}