一个有关汉诺塔的习题

把有$n$个圆盘的塔从左边的桩柱A移动到右边的桩柱B,不允许在A和B之间直接移动,求最短的移动序列.(每一次移动都必须是移动到中间的桩柱或者从中间的桩柱移出.像通常一样,较大的圆盘不能放在较小圆盘的上面).

看到这道题之后,我首先想到的是普通的汉诺塔的解法:

  1. 先借助C柱子把A柱子上的前n-1个圆盘移动到B柱子上
  2. 然后把A柱子上最底层的那个圆盘直接移动到C柱子上
  3. 最后借助A柱子把B柱子上的n-1个圆盘移动到C柱子上

但是这道题限制了A柱子上的圆盘不能直接移动到C柱子上,粗略看来,原来解法的第1步和第2步就是一个非法的操作了。然后......然后我的脑袋就卡壳了。

思索了一会儿实在想不出,就去翻看答案(泪)

答案很简略,基本就只给出了一个递推式:\[X_n=X_{n-1}+1+X_{n-1}+1+X_{n-1}\]并直接给出$X_n=3^n-1$.

不得不说当我第一眼看到这个式子的时候陷入了一阵茫然。一番思考之后,开始了破罐破摔的尝试,既然一开始A柱子上的圆盘不能借助C柱子,那就干脆借助B柱子先移动到C吧,但残酷的现实告诉我,我根本没有清晰的定义什么叫做“借助”,是否这样的借助就一定不会违反事先约定的规则呢?先考虑小规模的问题吧,模拟一下1、2、3个盘子的情形,这种“借助”确实可以在不违反规则的前提下完成约定的状态的转变。再仔细想一下,如果需要把第$i$个盘子移动到C,那么首先肯定需要将它移动到B,这一步是必不可少的,但完成这一步的前提是,它上面$i-1$个盘子都已经移动到了C柱子,这会违规吗?不知道。不过要把前$i-1$个盘子不违规的移动到C,首先得把第$i-1$个圆盘不违规的移动到C,嗯,那它也得先移动到B吧,于是完成这件事情的前提又变成前$i-2$个盘子能不违规的移动到C.......似乎感觉到了啥啊,这样追回去,只要第1个盘子能够从A柱子不违规的移动到C柱子,就行哒......是这样对吧?(不管你信不信,反正我信了- -)

所以现在的思路很明确了,要把这$n$个柱子从A移动到C,必须先将前$n-1$个盘子借助(可以放心大胆的用这词儿了)B移动到C,然后把第$n$个盘子从A柱子移动到B(不然你还想怎么去C呀- -),然后借助(由对称性,这里的“借助”也是well-defined的)B柱子,把C柱子上的$n-1$个圆盘移动到A柱子(酱子才能让B上的那个大圆盘去C嘛),最后再借助B柱子,把A柱子上的$n-1$个圆盘移动到C柱子上就完成了。同时我们可以看到,这里的每一步都是必须的,换种说法,这应该就是传说中的最短的移动方法。

现在再去看那个递推式子,肿么样,懂了吧?

顺便给出C++代码:

#include<iostream>
using namespace std;
long long count=0;
void Hanoi(int n,int p1,int p2,int p3)
{
    if(n==1)
    {
        cout<<p1<<"-->"<<p2<<endl;
        cout<<p2<<"-->"<<p3<<endl;
        count+=2;
    }
    else
    {
        Hanoi(n-1,p1,p2,p3);
        cout<<p1<<"-->"<<p2<<endl;count++;
        Hanoi(n-1,p3,p2,p1);
        cout<<p2<<"-->"<<p3<<endl;count++;
        Hanoi(n-1,p1,p2,p3);
    }
}
int main()
{
    Hanoi(2,1,2,3);
    cout<<count<<endl;
    return 0;
}

 

posted @ 2013-04-08 18:33  学习中的小毛  阅读(520)  评论(3编辑  收藏  举报