0

我正在尝试解决河内塔问题的变体,其中有三个钉子,但两个塔的高度和磁盘大小相同。任务是交换两个塔。

我的解决方案是将两个塔堆叠在一起成为一个大塔(相同大小的磁盘可以堆叠在彼此的顶部)并再次将它们分开(当然是交换钉)。

我能够将两座塔堆叠在一起,但我无法反转我的算法以再次将它们分开。

在这种情况下,有两个塔,每个塔都有三个磁盘。一个在左边,一个在中间。在我的算法之后,有一个塔,右边有六个圆盘。

我的算法如下:(我使用的是Java)

public void solve() {
    combineTo(3, 0, 1, 2); // parameters: (height, from, to, temp)
    splitUp(?, ?, ?, ?);
}

private void moveDisk(int from, int to){
    // here are also a few other things going on but 
    // that doesn't matter in this case
    System.out.println("from: "+from+" - to: "+to);
}

private void moveTower( int i, int from, int to, int temp) {
    if (i == 0) return;
    else{
        moveTower( i-1, from, temp, to );
        moveDisk(from, to);
        moveDisk(from, to);
        moveTower( i-1, temp, to, from );
    }
}

private void combineTo( int i, int from, int to, int temp ){
    if (i==0) return;
    else{
        combineTo(i-1, from, to, temp);
        moveDisk(to, from);
        moveTower(i-1, temp, to, from);
        moveDisk(from, temp);
        moveDisk(from, temp);
        moveTower(i-1, to, temp, from);
    }
}

private void splitUp( int i, int from, int to, int temp ){
    if (i==0) return;
    else{
        ???
    }
}

那么我该如何用这个splitUp方法来逆转呢?

4

2 回答 2

1

你有最难的部分!想想从一副牌的底部发牌。一旦它们组合成一个堆栈,只需将整个堆栈移动到您需要底部磁盘所在的位置。然后再次将整个堆栈减去底部元素移动到距离底部第二个磁盘需要的位置。等等。也许有更聪明的方法,但这肯定有效。

(你也可以通过一次处理两个底部的来进行拆堆,这与你进行堆叠的方式相反。这可能会更有效一些。)

这是一个带有简单文本图形的 C 版本。请注意,我使用了一种稍微不同的方式来构建单个堆栈。你的总动作效率更高一些。我们将正标记的磁盘与负标记的磁盘交换:

#include <stdio.h>

// Three pegs with various numbers of integer-labeled disks.
struct peg {
  int disks[30];
  int n_disks;
} pegs[3];

// Set up positive-labeled disks on peg 0 and negative ones on peg 1.
void init(int n_disks)
{
  for (int i = 0; i < n_disks; ++i) {
    pegs[0].disks[i] = n_disks - i;
    pegs[1].disks[i] = -(n_disks - i);
  }
  pegs[0].n_disks = pegs[1].n_disks = n_disks;
}

// Draw simple text graphic of pegs.
void show(void)
{
  printf("|\n");
  for (int i = 0; i < 3; i++) {
    printf("|");
    for (int j = 0; j < pegs[i].n_disks; j++)
      printf("|%2d|", pegs[i].disks[j]);
    printf("\n|\n");
  }
  printf("\n");
}

// Move one disk and draw the pegs.
void move_1(int a, int b)
{
  struct peg *peg_a = &pegs[a], *peg_b = &pegs[b];
  int disk = peg_a->disks[--peg_a->n_disks];
  peg_b->disks[peg_b->n_disks++] = disk;
  //printf("move disk %d from peg %c to peg %c\n", disk, 'A' + a, 'A' + b);
  show();
}

// Move top n disks of tower at from to to using tmp as storage.
void move(int n, int from, int to, int tmp)
{
  if (n == 0) return;
  move(n - 1, from, tmp, to);
  move_1(from, to);
  move(n - 1, tmp, to, from);
}

// Stack the towers 0 and 1 of height n into a single tower on 2.
void stack(int n)
{
  if (n == 0) 
    return;
  // Extra base case skips a couple of redundant moves.
  if (n == 1) {
    move_1(0, 2);
    move_1(1, 2);
    return;
  }
  stack(n - 1);
  move_1(0, 1);
  move(2 * (n - 1), 2, 0, 1);
  move_1(1, 2);
  move_1(1, 2);
  move(2 * (n - 1), 0, 2, 1);
}

// Swap contents of pegs 0 and 1 using 2 as temp storage.
void swap(void)
{
  stack(pegs[0].n_disks);
  int n = pegs[2].n_disks;
  move(n, 2, 1, 0);
  while (n > 0) {
    move(--n, 1, 0, 2);
    move(--n, 0, 1, 2);
  }
}

int main(void)
{
  int n = 3;
  init(n);
  show();
  swap();
  return 0;
}
于 2014-01-30T04:09:39.677 回答
0

正如吉恩所说,我已经经历了最难的部分。使用我在问题中提供的算法,我已经在右边有一大堆。

然后,我将带有经典 hanoi 算法的堆栈移到左侧,并添加了以下递归算法。

public void solve() {
    combineTo(i, 0, 1, 2); // combines 2 stacks to the right
    hanoi(2*i, 2, 0, 1); // moves big stack to the left
    splitTower(2*i, 0, 1, 2); // splits tower up again
}

private void splitTower( int i, int from, int to, int temp) {
    if (i == 0) return;
    else{
        hanoi(i-1, from, to, temp);
        splitTower( i-1, to, from, temp );
    }
}


private void hanoi( int i, int from, int to, int temp) {
    if (i == 0) return;
    else{
        hanoi( i-1, from, temp, to );
        moveDisk(from, to);
        hanoi( i-1, temp, to, from );
    }
}
于 2014-02-01T06:38:47.387 回答