动态规划算法之汉诺塔问题-其一

175 阅读3分钟

题目描述

传说中有一座位于印度寺庙的塔,塔内有三个柱子,其中一个柱子上摞着n个大小不同的圆盘,从底部开始呈递减的形式。目标是将这些圆盘按照规定的规则从初始柱子(通常是最左边的柱子)移动到目标柱子(通常是最右边的柱子),并且在整个过程中遵守以下规则:

1、每次只能移动一个圆盘。
2、不能将较大的圆盘放在较小的圆盘

求移动到右边柱子的最少需要的移动次数。

问题分析

以一个三阶汉诺塔为例,我们先观察其移动过程

再看下四阶的汉诺塔移动过程

从上面的两个例子中,我们可以归纳总结,n阶的汉诺塔,在移动时,都需要经过上面红框里面的三步

1、先将前面n-1个圆盘移到中间的柱子上;

2、再将最大的圆盘从左边移到右边的柱子上;

3、最后将中间柱子上的n-1个圆盘移到右边的柱子上;

而n-1阶又可以由n-2阶推导出,以此类推,最后可推到1阶汉诺塔,然后从1阶的最少移动次数为1出发,算出2阶的最少移动次数为 1+1+1,3阶的最少移动次数为 3+1+3,以此类推。其实不难发现,n阶汉诺塔的最少移动次数其实就等于(2^n) -1,写成函数表达式则是f(n)=f(n-1)+1+f(n-1)表达式中的三部分正好对应上面的三个步骤, 所以经过上述分析,怎么用代码实现是不是呼之欲出。

代码实现

/**
 *
 * @param n 代表汉诺塔阶数
 * @return 返回最少移动次数
 */
private int getHanoi(int n) {
    Stack src = new Stack();
    Stack mid = new Stack();
    Stack dest = new Stack();
    return compute(n,src,mid,dest);
}

/**
 *
 * @param n  代表汉诺塔阶数
 * @param src 左边的柱子
 * @param mid 中间的柱子
 * @param dest 右边的柱子
 * @return 返回最少移动次数
 */
private int compute(int n, Stack src, Stack mid, Stack dest) {
    if(n == 1) {
        return 1;
    }else {
        //先将前面n-1个圆盘移到中间的柱子上
       int result = compute(n-1,src,dest,mid);
       //再将最大的圆盘从左边移到右边的柱子上
       result++;
       //最后将中间柱子上的n-1个圆盘移到右边的柱子上
       result = result + compute(n-1,mid,src,dest);
       return result;
    }
}

解法2:动态规划

由前面的分析,我们知道n阶汉诺塔的函数表达式为f(n)=f(n-1)+1+f(n-1), 由此,我们可以直接复用建立状态转移方程f(n)=f(n-1)+1+f(n-1)

PS:使用动态规划算法时,状态转移方程有几个变量,我们就建立几维数组来存储中间结果

代码实现

/**
 *
 * @param n 代表汉诺塔阶数
 * @return 返回最少移动次数
 */
public int getHanoi(int n) {
    if(n == 1) {
        return 1;
    }
    int[] data = new int[n];
    data[0] = 1;
    for(int i = 1; i < n; i++){
        data[i] = data[i-1] + 1 + data[i-1];
    }
    return data[n-1];
}