汉诺塔问题

汉诺塔规则如下:
1、有三根相邻的柱子,标号为A,B,C。
2、A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘。
3、现在把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。

这是一个递推问题,请先看2个盘子的情况:

汉诺塔问题_第1张图片

第一次可将上层的块移动到B上:
汉诺塔问题_第2张图片
第二次可将A上的块移动到C上:
汉诺塔问题_第3张图片
第三次再将B上的块移动到C上:
汉诺塔问题_第4张图片
这样就实现了题目的要求,下面看到n个块的情形:
汉诺塔问题_第5张图片
如图所示,三角形部分有n-1块,最底下有1块,根据上个例子中情况,将上面的n-1块看做一个整体,当做只有2块的基本情况就可以实现。同样的,对于上面的n-1块,也可以分成n-2块和1块的子问题,如此递归下去即可求得答案。
代码如下:

#include
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.

请看例题

汉诺塔问题_第6张图片
汉诺塔问题_第7张图片

该题是汉诺塔的升级版,规则不变,只是增加了一个塔。现让你统计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 
#include 
#include 
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;
}

你可能感兴趣的:(算法竞赛进阶指南)