动态规划解决汉诺塔问题——清晰明了的思路
问题描述
有A,B,C三个柱子,A柱子上从上到下,从小到大排列着n个圆盘。现要求将A柱子上的n个圆盘全部移动到C柱子上,依然按照从上到下,从小到大的顺序排列。且对移动过程要求如下:
a)一次只能移动一个盘子。
b)移动过程中大盘子不允许出现在小盘子上方。
问:总共需要移动的步数是多少?
大致思路
思路简要如下:将移动步骤分为三步,
a)将最上面的n-1个盘子从A柱子移动到B柱子上
b)将最大的盘子移动到C柱子上
c)再将B柱子上的n-1个盘子移动到C柱子上
完成了。
好的,现在我们假设,f(n)
是将n个盘子从其中一个盘子移到另一个盘子的步数。
那么上面的三步就变成了:
a)所需f(n-1)
步
b)所需1步
c)所需f(n-1)
步
显然可以得到 f(n) = 2 * f(n - 1) + 1
,这是个啥呢,利用高中数列知识变成
f(n) + 1 = 2(f(n - 1) + 1)
,令q(n) = f(n) + 1
发现没有,q(n) 是一个等比序列。
q(n) = 2^n(n >= 1)
, f(n) = 2^n - 1
。
到此,问题解决。
int hanoi(int n){
return (1<<n)-1;
}
当然如果要打印路径,那么需要利用回溯。
int cnt=0; //步数
void move(int id, char from, char to) // 打印移动方式:编号,从哪个盘子移动到哪个盘子
{
printf ("step %d: move %d from %c->%c\n", ++cnt, id, from, to);
}
void hanoi(int n, char x, char y, char z) //函数的物理意义是打印n个盘子从x柱子到z柱子的路径。
{
if (n == 0)
return;
//下面三个过程与思路哪儿一样,或者说就是每一步思路的代码实现
hanoi(n - 1, x, z, y); //先要把上面的n-1个盘子从x柱子移动到y柱子,打印这个过程的路径
move(n, x, z); //然后把第n个盘子从x柱子移到z柱子,打印这次移动
hanoi(n - 1, y, x, z); //把y柱子上n-1个盘子从y柱子移动到z柱子,也打印这个过程的路径
}