汉诺塔介绍
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
普通汉诺塔
我们先来对汉诺塔的步数进行一下递推
对于n个盘子,我们可以把它分成n−1个盘子和最后一个大盘子
设F(n)为移动所需步数,那么对于n个盘子来说所做的事情就是将n−1个盘子借助C柱移动到B柱上,这一过程移动的步数为F(n−1) (F(n)其实就是将n个盘子借助B柱移动到C柱上,注意:借助柱和目标柱互换名称是不改变移动步数的)
下一步我们将大盘子移动到C柱上,此时需要一步,最后,我们再将n−1盘子借助A柱移动到C柱上,此时需要的步数仍为F(n−1)
综合以上分析得到:
F
n
=
2
F
n
−
1
+
1
(
F
1
=
1
,
n
≥
2
)
\begin{aligned}F_{n}=2F_{n-1}+1\\ \left( F_{1}=1,n\geq 2\right) \end{aligned}
Fn=2Fn−1+1(F1=1,n≥2)
对两边同时加上1可以凑成一个等比数列,然后就可以求出其通项公式:
F
n
=
2
n
−
1
(
n
=
1
,
2
,
…
)
\begin{aligned}F_{n}=2^{n}-1\\ \left( n=1,2,\ldots \right) \end{aligned}
Fn=2n−1(n=1,2,…)
代码:
#include <stdio.h>
#include <iostream>
#include <math.h>
using namespace std;
int ans = 0, n;
long long ans2 = 0;
void hmove(char a, char b, char c, int n){
if(n == 0){return ;}
hmove(a, c, b, n - 1); //前n - 1块从a移动到b上
printf("%c -> %c\n", a, c); //我自己移动一块从a -> c
ans++; //我移动了几次?
hmove(b, a, c, n - 1); //最后把 n - 1块从b移动到c上
}
int main(){
scanf("%d", &n);
printf("普通汉诺塔:\n");
hmove('a', 'b', 'c', n);
printf("%d\n", ans);
}
汉诺塔变式
限定条件:
限定圆盘只能够移动到相邻的柱子,即a柱子上的圆盘只能够移动到b,b柱子上的圆盘只能够移动到a或者c,c同理。
思路:
同样的,对于n个盘子,我们可以把它分成n−1个盘子和最后一个大盘子
同样设F(n)为移动所需步数, 将n−1盘子借助B柱移动到C柱上,这一过程移动的步数极为F(n−1),将大盘子由A柱移动到B柱上,此时需要一步,将n−1个盘子借助B柱移动到A柱上,这一过程移动的步数同样为F(n−1) 将大盘子由B柱移动到C柱上,此时需要一步,将n−1个盘子借助B柱子移动到C柱上,此时需要的步数仍为F(n−1)
综合以上分析,得到:
F
n
=
3
F
n
−
1
+
2
(
F
1
=
2
,
n
≥
2
)
\begin{aligned}F_{n}=3F_{n-1}+2\\ \left( F_{1}=2,n\geq 2\right) \end{aligned}
Fn=3Fn−1+2(F1=2,n≥2)
同样,对两边同时加上1可以凑成一个等比数列,最后求出其通项公式即为
F
n
=
3
n
−
1
(
n
=
1
,
2
,
…
)
\begin{aligned}F_{n}=3^{n}-1\\ \left( n=1,2,\ldots \right) \end{aligned}
Fn=3n−1(n=1,2,…)
代码:
void hmove2(char a, char b, char c, int n){
if(n == 0){
return ;
}
hmove2(a, b, c, n - 1); //把前n - 1块通过b移动到c上
ans2++; //把最后一块移动到b上
printf("%c -> %c\n", a, b);
hmove2(c, b , a, n - 1); //把n - 1 块通过b移动到a上
ans2++; //把最后一块移动到c上
printf("%c -> %c\n", b, c);
hmove2(a, b, c, n - 1); //把剩下的n - 1块通过b移动到c上
}