汉诺塔规则如下:
1、有三根相邻的柱子,标号为A,B,C。
2、A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘。
3、现在把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。
这是一个递推问题,请先看2个盘子的情况:
所以递推公式为d[i] = 2d[i-1] + 1.
请看例题
1、有三根相邻的柱子,标号为A,B,C。
2、A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘。
3、现在把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。
这是一个递推问题,请先看2个盘子的情况:
第一次可将上层的块移动到B上:
第二次可将A上的块移动到C上:
第三次再将B上的块移动到C上:
这样就实现了题目的要求,下面看到n个块的情形:
如图所示,三角形部分有n-1块,最底下有1块,根据上个例子中情况,将上面的n-1块看做一个整体,当做只有2块的基本情况就可以实现。同样的,对于上面的n-1块,也可以分成n-2块和1块的子问题,如此递归下去即可求得答案。
代码如下:
#include<iostream>
using namespace std;
void hanoi(int n, char A, char B, char C) {//把A顶层的盘子跨过B移到C上
if (n == 1) {
printf("%c -> %c\n", A, C);
}
else {
hanoi(n - 1, A, C, B);
printf("%c -> %c\n", A, C);
hanoi(n - 1, B, A, C);
}
}
int main() {
hanoi(2, 'A', 'B', 'C');
return 0;
}
下面请思考,对于n个盘子,3个塔的汉诺塔,至少需要移动多少次才能完成呢?其实这是一个动态规划的问题,只有1个块时显然d[1] == 1, 2个块时应该是2d[1] + 1,这是因为,移动上层的块到B,需要d[1]次,然后将最底层的块移到C,需要1次,再将B层的块移动到C又是d[1]次。所以递推公式为d[i] = 2d[i-1] + 1.
请看例题
该题是汉诺塔的升级版,规则不变,只是增加了一个塔。现让你统计n个盘子的移动次数。同上面的思考一样,这也是动态规划。n个盘子4个塔的最小移动次数递推公式为
f[n] = min(f[n], f[j]*2 + d[n-j]);
该公式可这样理解:对于搬运n个盘子可看做三步,第一步搬运前j个盘子到其他位置,因为有4个塔可以选择,所以此时有f[j]个操作;第二步,搬运剩下的n-j个盘子到与第一步不同的另一根塔上,否则盘子就不是从小到大了,此时只有3个塔可以选择,所以是d[n-j];最后再将第一步搬运的j个盘子放到最后一根塔上,此时有4个塔可供选择,所以是f[j]。三步的总次数就是所求答案。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int d[15], f[15];//d表示有三个塔供选择的时候;f表示有四个塔供选择
int main() {
d[1] = 1; //3个塔,1个盘子时显然是1
for(int i = 2; i <= 12; i++) {
d[i] = 1 + d[i-1] * 2;
}
memset(f, 0x3f, sizeof f);//将f初始化为较大的数
f[0] = 0;//下面递推式中要用到,所以要初始化为0
for(int i = 1; i <= 12; i++) {//对4个塔12个盘子塔的情况逐一枚举
for(int j = 0; j < i; j++) {//每次从第1个塔上拿出j个盘子放到其他塔上
f[i] = min(f[i], f[j] * 2 + d[i - j]);//搬运j个盘子的时候有4个塔可供选择,所以是f[j];搬运剩下的i-j个盘子时,只有3个
//塔是可以选择的,否则就不是盘子从小到大的顺序了。
}
}
for(int i = 1; i <= 12; i++) {
cout << f[i] << endl;
}
return 0;
}