美文网首页
汉诺塔问题

汉诺塔问题

作者: 愤怒的熊猫V | 来源:发表于2019-08-10 17:22 被阅读0次

    一个n层的汉诺塔,从A移动到C

    由于汉诺塔问题本身的限制,我们最先能思考到的点是第n层最后肯定是要放在C的最下面的

    有了这个思考后,我们又想,要想使汉诺塔移动次数最小

    —— —— —— ——                          ???                                        空         

             A                                                    B                                            C

    那么我们就要满足可以A中只剩下n这一张圆盘了,而C中是空的,那么我们就可以直接将A中这一张圆盘直接放入C中

    OK,A.C的结构确定了,那B的结构是什么呢?

    很显然,由于汉诺塔的特性,所以B中此时是n-1层的汉诺塔

    我们再将B中的n-1层汉诺塔移动到C中即可,这个操作完全等同于将n-1层汉诺塔从A中移动至C,且B,C为空这一初始条件

    因为B中的n-1层圆盘本身满足汉诺塔结构,且第n-1层是小于C中的第n层的也就是可以将C也看成是空的,所以我们可以乱放,怪放,无限放,终极放,究极皮皮放!

    OK,我们还要考虑的是从A中移动n-1层到B,这个问题和从A中移动n-1层到C完全一样!!!

    所以抛开第n个圆盘从A移动到C这一个操作外

    我们还有从A移动整个n-1层汉诺塔到B,然后从B将整个n-1层汉诺塔移动到C的这两步操作!

    OK,无论你叫他递归问题也好,DP问题也好,我觉得更像是递归问题,但我还是写成DP的样子把

    DP[n] = 2*DP[n-1]+1

    写成递归就好

    def hanoi(n):

        if n == 1:

            return 1

        result = 0

        result += hanoi(n-1)

        return result


    如果告诉你汉诺塔盘随机地分布在三个柱子上(前提条件是满足每个柱子上依然满足汉诺塔结构),然后问当前状态是不是汉诺塔从A移动到C的必经状态,如果不是返回-1,如果是,返回是当前的第几步

    可以这样思考,假设第二个柱子上出现了n号盘子,我们可以肯定的是它不是必经状态因为第n号盘子肯定是直接从A移动到C的

    OK有了这个思路就好办了

    我们记主柱子为main,辅助柱子为sup,目的柱子为tar

    这样记录是有目的的

    假设三个汉诺塔是这样的结构

    A:main:[X    X]

    B:sup:[X    X]

    C:tar:[X    X]

    假设总共有6号盘子,由上面的结论,从A移动6个盘子到C中总共需要2^6-1 = 63种

    我们检查最大的数6在哪个位置

    我们知道的是从A将第6号盘子移动到C中是第(63+1)//2   +1  =   32步,

    如果6处在sup中,肯定返回-1,

    不是的话,我们再看6处在哪个位置

    假设6处在tar中,我们可以认为,它可能是将n-1层汉诺塔从sup(B)移动到tar(C)的一个中间过程

    那么这个状态就有可能是第34步到第63步中的某一步

    记住我们现在的目标是从B中将5层汉诺塔移动到C中,那么此时就与第6个汉诺塔无关

    我们将tar中队列最前面的数字弹出tar.pop(0)

    且现在的主柱子是B,目标柱子是C,辅助柱子是A,即

    A:sup[X    X]

    B:main[X    X]

    C:tar[X]

    同样的,我们看5在哪,如果5在A中,即辅助柱中,肯定没戏,如果在主柱子中,那么肯定在第32到(63-32+1)//2 +1步中

    如此循环

    因此我们的程序应该是这样的

    因为从A移动n个到C中,所以我们记A为主,B为辅,C为目标,且把所有的盘子按1到n进行标号,

    将状态放入A.B.C三个列表中

    main = [X,X,X,X,X]

    sup   = [X,X,X,X,X,X,X]

    tar     = [X,X,X,X]

    两个指针记录区间

    left = 1,right = 2^n-1

    当left和right重合时,就是找到的次数

    while    left!  !=    right:

            #如果n在辅助队列中直接返回不在必经之路上

            if    n    in    sup:

                    return -1

            #如果n在主队列中,将右区间缩减到原区间的终点的左侧

            #且    将目标队列和辅助队列交换位置

            #且    将n从main中弹出

            elif    n    in    main:

                    right    =    (left+right)//2-1

                    main.pop(0)

                    sup,tar    =    tar,sup

             elif    n    in    tar:

                        left    =    (left+right)//2+1

                        tar.pop(0)

                        sup,main    =    main,sup

    reuturn left


    考虑了一会儿,发现上面一个漏洞

    我们的left和right都是指移动了多少步

    而最原始的状态应该表示移动了第0步

    所以left的初始值应为0

    相关文章

      网友评论

          本文标题:汉诺塔问题

          本文链接:https://www.haomeiwen.com/subject/fjuhjctx.html