汉诺塔问题进阶

题目:

给定一个整形数组arr,其中只含有1、2和3,代表所有圆盘目前的状态。1代表左柱,2代表中柱,3代表右柱,arr[i]代表第i+1个圆盘的位置。比如,arr=[3,3,2,1],代表第一个盘子在右柱上,第二个盘子在右柱上,第三个圆盘在中柱上,第四个圆盘在左柱上。如果arr代表的状态是最优移动轨迹过程中出现的状态,返回arr这种状态是最优移动轨迹中的第几个状态。如果arr代表的状态不是最优移动轨迹过程中出现的状态,则返回-1.

思路:

首先设from柱子上的圆盘为1~i-1,如果移动到to上的最少步骤数,假设为S(i)。根据此前汉诺塔的解题步骤可得,S(i)=步骤1的步骤总数+1+步骤3的步骤总数= S(i-1)+1+S(i-1),由于S(1)=1.所以S(i)+1 = 2(S(i-1)+1),根据等比数列的求和公式可得S(i)+1 = 2^i,所以S(i)=2^i-1.

在arr数组中,N-1的位置至关重要。要分几种情况进行考虑:

  • 第N-1个圆盘在from柱子上,则需要考虑1~N-2个柱子的情况。
  • 如果第N-1个圆盘在mid柱子上,则返回-1.因为按照汉诺塔的最佳移动方式,最后一个圆盘是不可能出现在中间的。
  • 如果第N-1个圆盘出现在to柱子上,则有两种可能,1~N-2个圆盘都在mid柱子上,或者在mid到to的过程中。此时的柱子最少已经走完了2^i-1步。由于from上的最后一个圆盘转移到了to柱子上因此还需要+1,则最少走完的步数为2^i-1.之后需要考虑1~N-2个圆盘的情况。

实现代码:

public int step(int [] arr){
    if(arr == null || arr.length == 0){
        return -1;
    }
    return process(arr,arr.length-1,1,2,3);
}

public int process(int [] arr,int i,int from,int mid,int to){
    if(i==-1){
        return 0;
    }
    if(arr[i] != from && arr[i] != to){
        return -1;
    }
    if(arr[i] == from){
        return process(arr,i-1,from to,mid);
    }else{
        int rest = process(arr,i-1,mid,from,to);
        if(rest == -1){
            return -1;
        }
        return (1<<i)+rest;
    }
}

非递归的写法:

public int step(int [] arr){
    if(arr == null || arr.length == 0){
        return -1;
    }
    int from = 1;
    int mid = 2;
    int to = 3;
    int i = arr.length-1;
    int res = 0;
    int tmp = 0;
    while(i >= 0){
        if(arr[i] != from && arr[i] != to){
            return -1;
        }
        if(arr[i] == to){
            rest += 1<<i;
            tmp = from;
            from = mid;
        }else{
            tmp = to;
            to = mid;
        }
        mid = tmp;
        i--;
    }
    return rest;
}
    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/G7S9R/article/details/78571360
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞