算法:汉诺塔的最优步数

题目描述

给定一个数组arr,长度为N,arr中的值只有1,2,3三种

  • arr[i] == 1,代表汉诺塔问题中,从上往下第i个圆盘目前在左
  • arr[i] == 2,代表汉诺塔问题中,从上往下第i个圆盘目前在中
  • arr[i] == 3,代表汉诺塔问题中,从上往下第i个圆盘目前在右

那么arr整体就代表汉诺塔游戏过程中的一个状况

如果这个状况不是汉诺塔最优解运动过程中的状况,返回-1

如果这个状况是汉诺塔最优解运动过程中的状况,返回它是第几个状况

题目解析

问题:完成 i i i层汉诺塔一共需要几步

假设一共有三个柱子:from、to、other

对于 i i i层汉诺塔,整个移动过程可以分为三步:

  • 第一步: 将 [ 1 , i − 1 ] [1,i-1] [1i1]从from移动到mid
  • 第二步:将 [ i ] [i] [i]从from移动到to
  • 第三步:将 [ 1 , i − 1 ] [1,i-1] [1i1]从mid移动到to

从上面三个部分可以知道
s [ i ] = s [ i − 1 ] + 1 + s [ i − 1 ] s[i] = s[i - 1] + 1 + s[i - 1] s[i]=s[i1]+1+s[i1]

化简后可得等比数列:
s ( i ) = 2 s ( i − 1 ) + 1 s(i) = 2s(i - 1) + 1 s(i)=2s(i1)+1

用数学归纳法得出结论:👇
s ( i ) = 2 i − 1 − 1 s(i) = 2^{i - 1} - 1 s(i)=2i11

3层汉诺塔,用了7步,每一步都走最优解的话,那我们步数就是2^i-1步

class Solution {


public:
    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;
    }

    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作为中转,最优解是多少步???
    }
};

当前是第几步

3层汉诺塔

  • arr=111,表示汉诺塔问题最开始状态都在1棍子上,这是第0步
  • arr=311,表示汉诺问题第1步,0号盘放到了3棍子上
  • arr=321,表示汉诺塔问题第2步,1号盘放到了2棍子上
  • ……
  • arr=333,这个arr是汉诺塔问题的第2^3-1步

问题:那么现在来到了i号盘,我们让i盘子去哪里?

  • 如果是最优解,此时我们已经让 i − 1 i - 1 i1层汉诺塔到了other了,那么必须让它去to,这就是最优解,i非要去other绕弯子,返回-1,不合法的。

现在我们定义一个函数 f ( a r r , i , f r o m , t o , o t h e r ) f(arr,i,from,to,other) f(arr,i,from,to,other):arr状态带着,i+1层汉诺塔问题,0–i为编号,把i+1层全部搬到to上,看看arr是最优解的第几步?

此时我们来看 i i i号盘在哪里

  • 如果 i i i o t h e r other other上,那么不是最接解,return -1
  • 如果 i i i f r o m from from上,也就是在汉诺塔的第一步了
    • 它还需要完成1),然后2)才能去完成3)
    • 也就是需要将i-1搬到other,即 f ( a r r , i − 1 , f r o m , o t h e r , t o ) f(arr,i-1,from,other,to) f(arr,i1,from,other,to)
  • 如果 i i i t o to to
    • 也就是2)已经完成了,需要完成3)然后统计步数返回
class Solution {


public:
    int process(std::vector<int>arr ,int i, int from, int to, int other){
        if (i == -1) return 0;//没有盘子了,不走,返回0步

        //否则还有盘子,0号盘是最小盘子
        if (arr[i] == other) {
            return -1;
        }
        if (arr[i] == from) {//i竟然还在from,说明这1)也没有搞定,需要先搞定1)
            return process(arr, i - 1, from, to, other);
        } else { // arr[i] == to
            // 已经走完1,2两步了,
            int p1 = (1 << i) - 1;
            int p2 = 1;
            int p3 = process(arr, i - 1, other, to, from);
            if (p3 == -1) {
                return -1;
            }
            return p1 + p2 + p3;
        }
    }

    int bestStepHanNuo(std::vector<int> arr){
        int N = arr.size();
        return process(arr, N - 1, 1, 3, 2);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值