[英]How to implement a recursive algorithm backwards
我正在嘗試解決“河內塔樓”問題的一種變體,其中有三個釘子,但兩個塔樓的高度和磁盤大小相同。 任務是交換兩個塔。
我的解決方案是將兩個塔堆疊到一個大塔上(相同大小的磁盤可以堆疊在彼此的頂部),然后再將它們拆分(當然也要轉換成釘子)。
我能夠將兩個塔堆疊在一起,但無法反轉算法以再次拆分它們。
在這種情況下,有兩個帶有三個磁盤的塔。 一個在左邊,一個在中間。 在我的算法之后,右樁上有一塔,上面有六個磁盤。
我的算法如下:(我正在使用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
方法將其splitUp
呢?
你有困難的部分! 想想從甲板底下發牌。 將它們組合到一個堆棧中后,只需將整個堆棧移動到需要底部磁盤的位置即可。 然后再將整個堆棧減去底部元素再移到需要從底部開始第二個磁盤的位置。 等等,也許這是一個更聰明的方法,但這肯定行得通。
(您也可以通過一次從底部處理兩個來進行拆棧,這與您進行堆疊的方式相反。這可能會更有效率。)
這是帶有簡單文本圖形的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;
}
正如吉恩所說,我已經經歷了最艱難的時期。 使用我在問題中提供的算法,我已經在右側有了一大堆堆棧。
然后,我將具有經典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 );
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.