汉诺塔问题1与2:若arr是汉诺塔最优走法的其中一步,请返回这是第几步?

汉诺塔问题1与2:若arr是汉诺塔最优走法的其中一步,请返回这是第几步?

提示:汉诺塔问题有2个,一个正向理解,一个反向解题
这俩都要会
1简单,2难,但是1也是理解2的关键所在


汉诺塔问题1

请你把N层汉诺塔,按照最优解,把所有盘子,从1号棍子搬到3号棍子,依次输出搬移顺序;
小号盘子不能被压在大号盘子下面,最优解意味着最大号那个盘子,它不能去别的棍子上绕,
当它是最大号时,必须直接从1号棍子直接搬到3号棍子
【即从起始点1号棍子,直接搬到终点3号棍子,不能去2号棍子上绕——最优解】


一、审题

示例:不妨设N=3,即3层汉诺塔问题,这是最基本的例子,看图
让你,把3层汉诺塔,编号为012这仨盘子,想办法合法搬移,从1号棍子挪到到3号棍子上。
图1


二、解题

汉诺塔的搬移原则:
起点是from棍子【如本题中1号】,
终点是to棍子【如本题中3号】,
另外一根棍子让位用的叫other棍子【如本题中2号】

搬移i层汉诺塔【编号为0—i-1】,我们定义这一样一个搬移函数:
f(i, from, to, to),把i层汉诺塔从from,搬到to,中间other做让位:
如果是3层汉诺塔:则最大号盘子叫i-1=2号盘,即012
现在:from=1,to=3,other=2

第1步:将0号盘从1搬到3
第2步:将1号盘从1搬到2
第3步:将0号盘从3搬到2
——注意,这几步干了什么?将01两个盘,转移到让位other=2上
第4步:将2号盘从1搬到3
——注意,这一步干了什么?将2号盘,从from=1搬到了to=3上,这就是N层汉诺塔最大这个盘子
其实就是先让N-1层汉诺塔,让位到other上,我再搬最大这个盘子
第5步:将0号盘从2搬到1
第6步:将1号盘从2搬到3
第7步:将0号盘从1搬到3
——注意:这几步干了什么?将01两个盘,转移到to上,最终完成汉诺塔问题
图2
总结汉诺塔,要把0–i-1搬到目的地,需要干这三个大步骤:
是这样搬的,大逻辑:i层汉诺塔,
(1)第一个大步骤:将i-1层汉诺塔先让位,从from转移到other上;
(2)第二个大步骤:现在from上只剩下i了,把i从from转移到to上【这是最终目的,也是最优解】;
(3)第三个大步骤:然后将让位那i-1层汉诺塔,将其从other上转移到to;完成i层汉诺塔的事情
发了案例中用了几步吗?
3层汉诺塔,用了7步,每一步都走最优解的话,那我们步数就是2^i-1步【这个我们在汉诺塔问题2会用的】

好,看懂了案例,也知道了汉诺塔移动最优解的原则之后,我们来设计f函数:
process=f

//现在,这样看,将起点定位from,终点定位to,将让路的点定位other
    //终止条件还是N==1,将from直接移动到to
    //其余的,都是相互变换角色
    //先将N-1个盘,从from移动到other处,让路
    //然后将N从from移动到to
    //最后,将N-1个盘从other移动到to
    //完成汉诺塔
    public static void hanNuoTa2(int N){
        if (N <= 0) return;
        process(N, "左", "右", "中间");
    }

    public static void process(int N, String from, String to, String other){
        //终止条件
        if (N == 1) {
            System.out.println("将 1 号盘,从"+ from +"移动到"+ to);
            return;
        }
        //否则先让N-1个盘让路到other,然后把N从from移动到to,再让N-1个盘从other处移动到to
        process(N - 1, from, other, to);
        System.out.println("将 "+ N +" 号盘从"+ from +"移动到"+ to);
        process(N - 1, other, to, from);
    }

    public static void test2(){
        hanNuoTa2(2);
    }

    public static void main(String[] args) {
        test2();
    }

解释一下,N=1就输出是我们将N层汉诺塔编号叫123—N
等同编号为012–N,一个道理
无非就是写0号盘子开头,或者写1号盘子开头而已!

好,现在我们可以简单地将一个n层汉诺塔最优步数用递归算一下:

//不妨设:f(i,from,to,other)是将0--i号盘全部从from搬移到to,利用other中转,最后走的步数

    public static int f(int i, int from, int to, int other){
        if (i == -1) return 0;//没有盘子了,不走,返回0步

        //否则还有盘子,0号盘是最小盘子
        int step1 = f(i - 1, from, other, to); //走第1)步
        int step2 = 1;//完成2)
        int step3 = f(i - 1, other, to, from);//完成3)

        return step1 + step2 + step3;
    }

    //汉诺塔问题1的调用方式
    public static int bestStepHanNuo(int N){
        if (N == 0) return 0;//没有盘子不走
        if (N == 1) return 1;//一个盘子,走一步到位

        return f(N - 1, 1, 3, 2);//将0--N-1号盘子从1搬到3,将2作为中转,最优解是多少步???
    }

汉诺塔问题2

给你一个arr数组,每一个arr[i]代表棍子的编号【最简单的3根棍子,编号123】,意味着i号盘子,现在正在arr[i]棍子上。
这个arr显然就是代表汉诺塔搬移过程中的某一个步骤【状态】,
如果arr是汉诺塔问题最优走法的其中一步,请问你这是第几步?
返回arr是最优走法的第几步,如果不是最优走法的步骤,请返回-1。


三、审题

有了汉诺塔问题1,就很容易理解汉诺塔问题2

示例:arr=1111111——3333333
最开始都在1号棍子上,0–6盘子,显然这个arr=111111是汉诺塔问题的第0步;
最后搬到to上之后,都在3号棍子上,3333333这个arr是汉诺塔问题的第2^7-1步
图3


四、解题

再看例子:3层汉诺塔问题,盘子编号为012
arr=111,表示汉诺塔问题最开始状态都在1棍子上
arr=311,表示汉诺问题第1步,0号盘放到了3棍子上
arr=321,表示汉诺塔问题第2步,1号盘放到了2棍子上
……
tu4

那么现在来到了i号盘,我们让i盘子去哪里?
如果是最优解,我们既然已经让i-1层汉诺塔让位到other了,你i就直接去to吧,这就是最优解,你i非要去other绕弯子,没卵用,返回-1,不合法的。
图5
现在我们定义一个函数f(arr,i,from,to,other):arr状态带着,i+1层汉诺塔问题,0–i为编号,把i+1层全部搬到to上,看看我arr是最优解的第几步?

此时来看i号盘在哪呢?

(1)如果i号盘在other上,不好意思,违规,return -1;
(2)如果i号盘在from上,这说明压根汉诺塔的第1步还没有完成,直接返回f(arr,i-1,from,other,to);
——啥意思?也就是说,你i都还在from上,显然此时你在第几步?你在将i-1层汉诺塔让位到other上,步骤就只需要算汉诺塔问题的第1步那么多,直接返回f(arr,i-1,from,other,to)。看清楚,这里的目的地是让位other!
(3)如果i号盘在to上,说明你干完了所有汉诺塔问题的3步,即让位i-1层,把i放到to(1步),然后把让位那i-1层搬到to上,故返回:
2^(i)+1+f(arr,i-1,other,to,from)。看清楚,这里的真实目的地是to,起点是other!
为啥是i次方,因为0–i-1编号就i层汉诺塔
注意:f(arr,i-1,other,to,from)=-1时,无效!!!

主函数怎么调用,我们要调N-1号开始的汉诺塔问题,因为我们是0–N-1编号的
遇到i==-1,说明没有盘子了,那返回0步就行

//好,有了汉诺塔1的解,现在,通过arr表示汉诺塔了,arri可以表示i号盘在哪个位置,明白了吗,i号盘作为下标,它所在的位置是arri
    //这不就很好办了吗,我们要看违规的情况,
    //如果arri在other,显然违规,不是最优解
    //如果arri在from,显然,它还需要完成1),然后2)才能去完成3)
    //如果arri在to,显然2)都已经完成了,需要完成3)然后统计步数返回
    //记住n层汉诺塔问题最优解步数是2^n-1步
    //不妨设f2(arr,i,from,to,other)0--i汉诺塔问题要把它从from搬到to,请问如果arr是其中一步的话,返回步数,否则违规就返回-1
    public static int f2(int[] arr, int i, int from, int to, int other){
        if (i == -1) return 0;//没盘子了

        if (arr[i] != from && arr[i] != to) return -1;//i来到的位置既不是from也不是to就是other呗,违规,返回-1
        else if (arr[i] == from) return f2(arr, i - 1, from, other, to);//i竟然还在from,说明这1)也没有搞定,需要先搞定1)
        else {
            //arr[i] == to,2)都已经搞定了,还需要搞定3)
            int step3 = f2(arr, i - 1, other, to, from);
            if (step3 == -1) return -1;//如果搞定3)也违规,返回-1
            return (1 << i) - 1 + 1 + step3;
            //(1 << i) - 1是0--i-1号盘子,i层哦,所以最优解是2^i - 1
            //+1,是搞定2)
            //step3是搞定步骤3
        }
    }

    public static int bestStepHanNuo2(int[] arr){
        if (arr == null || arr.length == 0) return 0;
        int N = arr.length;//N层汉诺塔,下标是0--N-1

        return f2(arr, N - 1, 1, 3, 2);
    }

测试:

public static void test(){

        int[] arr = {3,2,1};//我已经搬好了,这样的话,步数最优解一定是和汉诺塔问题1一样的
        System.out.println(bestStepHanNuo(arr.length));
        System.out.println(bestStepHanNuo2(arr));
    }

    public static void main(String[] args) {
        test();
    }

总结

提示:重要经验:

1)汉诺塔问题1要熟悉原理
2)汉诺塔问题2要知道i在哪个盘上,判定此时处于哪个步骤上,干了汉诺塔问题1的哪几个步骤?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值