【问题标题】:Solving tower of Hanoi with given arrangements给定安排的河内解决塔
【发布时间】:2020-06-06 17:21:28
【问题描述】:

我得到了一个有效的 Hanoi 磁盘塔的排列。排列以数组形式给出。

例如[2,2,0]:数组的索引是磁盘的标识符,从小到大排序,数组的值是对应磁盘的位置(位置总是0, 1 或 2)。在[2,2,0]的情况下,最小的两个圆盘在第三极,而最大的圆盘在第一极:

      |           |           |
      |           |          [0]
   [22222]        |         [111] 
  ----+----   ----+----   ----+----

另一个例子:[0,2,1]

      |           |           |
      |           |           |
     [0]       [22222]      [111] 
  ----+----   ----+----   ----+----

是否可以递归地解决将所有磁盘移动到目标极点(第二极点)所需的剩余步骤?

public int solveForSteps(int[] disks){
    // Is there a possible way to solve with given arrangements?
}

【问题讨论】:

  • 看看关于在 SO stackoverflow.com/questions/ask 上发布问题的问题
  • 令人困惑。尝试更清楚地说明问题。
  • 杆和钉的意思是一样的。在河内的塔中,只有三个杆/钉。光盘是移动的东西。考虑到这一点,请您澄清您的问题并解释您的数组表示法吗?

标签: java algorithm recursion towers-of-hanoi


【解决方案1】:

要从任意位置求解河内塔,您可以使用类似于从标准起始位置开始的标准解法的递归过程。

它只需要更通用一点。

编写一个递归过程 moveDisks(maxSize,targetPeg) 将所有大小为 ma​​xSize 的磁盘移动到 peg targetPeg,例如这个:

  1. 找到最大的磁盘 m 使得 m.size 和 m 不是targetPeg 上。如果没有这样的磁盘,则返回,因为所有大小 ma​​xSize 的磁盘都已经在正确的位置。

  2. sourcePeg 为当前 m 所在的挂钩,并令 otherPeg 为不是 的挂钩>sourcePegtargetPeg

  3. 递归调用 moveDisks(m.size-1, otherPeg) 将较小的磁盘移开。

  4. msourcePeg 移动到 targetPeg

  5. 递归调用 moveDisks(m.size-1, targetPeg) 以将较小的磁盘放在它们所属的位置。

在Java中,我会这样写:

/**
 * Solve towers of hanoi from an arbitrary position
 * 
 * @param diskPositions the current peg for each disk (0, 1, or 2) in increasing
 *                      order of size.  This will be modified
 * @param disksToMove  number of smallest disks to moves
 * @param targetPeg target peg for disks to move
 */
static void moveDisks(int[] diskPositions, int disksToMove, int targetPeg)
{
    for (int badDisk = disksToMove-1; badDisk >= 0; --badDisk) {
        int currentPeg = diskPositions[badDisk];
        if (currentPeg != targetPeg) {
            // found the largest disk on the wrong peg

            // sum of the peg numbers is 3, so to find the other one:
            int otherPeg = 3 - targetPeg - currentPeg;

            // before we can move badDisk, we have to get the smaller
            // ones out of the way
            moveDisks(diskPositions, badDisk, otherPeg);

            // Move
            diskPositions[badDisk] = targetPeg;
            System.out.println(
                "Move " + badDisk + " from " + currentPeg + " to " + targetPeg
            );

            //Now we can put the smaller ones in the right place
            moveDisks(diskPositions, badDisk, targetPeg);
            break;
        }
    }
}

...好吧,在现实生活中我不会完全这样写。您实际上可以删除第二个递归调用和中断,因为循环中剩余的迭代将完成同样的事情。

【讨论】:

    【解决方案2】:

    我没有适合你的递归解决方案。当您查看河内塔的常用递归算法时,实际状态可能发生在递归树的深处,如果您想象这样一个状态被传递给您的函数,那么从该状态进一步解决问题不需要只是一个递归调用,还重建了“外部”递归调用的堆栈。这似乎使它变得相当复杂。

    但您可以反复进行。这是一种方法:

    static void solveForSteps(int[] disks) {
        int n = disks.length;
        // Calculate the next target rod for each of the disks
        int target = 2; // The biggest disk should go to the rightmost rod
        int[] targets = new int[n];
        for (int i = n - 1; i >= 0; i--) {
            targets[i] = target;
            if (disks[i] != target) {
                // To allow for this move, the smaller disk needs to get out of the way
                target = 3 - target - disks[i];
            }
        }
        int i = 0;
        while (i < n) { // Not yet solved?
            // Find the disk that should move
            for (i = 0; i < n; i++) {
                if (targets[i] != disks[i]) { // Found it
                    target = targets[i]; // This and smaller disks should pile up here 
                    System.out.format("move disk %d from rod %d to %d.\n", i, disks[i], target);
                    disks[i] = target; // Make move
                    // Update the next targets of the smaller disks
                    for (int j = i - 1; j >= 0; j--) {
                        targets[j] = target;
                        target = 3 - target - disks[j];
                    }
                    break;
                }
            }
        }
    }
    

    这将打印将所有磁盘移动到最右边的极点所需的剩余移动。相反,如果您想瞄准中心极点,则只需将 int target = 2 更改为 int target = 1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 2013-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-25
      相关资源
      最近更新 更多