漢諾塔問題詳解


【問題背景】

A柱子上有a個從上到下半徑依次遞減的圓盤。
A是初始柱子 B是空柱子 C也是空柱子
你要求把A上的a個圓盤都放到C柱子上去
並且C柱子上最后的圓盤的次序也同初始的A柱子一樣
在移動盤子的過程中,不能將大盤子放在小盤子上面
一次只能移動一個圓盤

【詳解】

這個問題可以分為三個步驟:
1.將A柱子上上面的a-1個圓盤全都移動到B柱子上.
2.把A柱子上唯一的一個圓盤移動到C柱子上。
3.把B柱子上的a-1個圓盤全都移動到C柱子上。
需要注意的是,第1步和第3步實際上都是遞歸進行的。

我們先來看第1個步驟。
把A柱子上面的a-1個圓盤移動到B柱子上。
這個時候,我們會發現這是原問題的一個縮小版,都是3個柱子,只不過第一個柱子上你現在
只需要考慮最上面的a-1個圓盤了。然后條件還是一樣,B,C柱子是空的,讓你把這a-1個圓盤放到
[B]柱子上去(對!目標的柱子改了!)
但他本質上還是同一個問題,所以我們可以寫出這么一個遞歸程序了
(a,b,c表示A,B,C柱子上每個柱子的圓盤個數)

void solve(char A,char B,char C,int a,int b,int c){
  if (a==0){
    說明沒有任何圓盤要移動,直接return;
  }
  solve(A,C,B,a-1,c,b);//把A柱子上的a-1個盤子全都移動到B柱子上去,此時輔助的柱子是原來的C柱子了
  //注意 函數的第三個參數總是圓盤的目標柱子,第二個參數總是輔助柱子
  ....
}

ok第一個步驟完成了。
(你們可能覺得 好像啥事情都沒做啊)
但不要忘記了,我們這個solve(A,B,C,a,b,c)的任務就是把第一個參數代表的柱子上的盤子全都放到第三個參數所代表
的柱子上去,輔助柱子是第二個參數所代表的柱子。
(我之所以強調是第i個參數,是因為第一個參數並不總是代表A柱子.

所以我們應該信任sovle(A,C,B,a-1,c,b)這個遞歸過程,他確實幫我們完成了這件事的第一個步驟。
那么緊接着,第二個步驟

把A柱子上唯一的一個盤放到C柱子上去。

void solve(char A,char B,char C,int a,int b,int c){
  if (a==0){
    說明沒有任何圓盤要移動,直接return;
  }
  solve(A,C,B,a-1,0,0);
  cout<<A<<"柱子上最頂端的圓盤直接放到"<<C<<"柱子上";
  ... }

接下來進行第三步,再遞歸地把第2根柱子上的a-1個盤子放到C柱子去.
這時你會發現,C柱子上那唯一的一個圓盤,實際上可以不用管它,因為他是最大的,所以誰放它上面都
無所謂,因此,我們可以就把它看成是空的柱子。
(而A柱子因為我們把它唯一的一個圓盤(在整個問題中可能這個柱子上不止有一個圓盤,但對於它這個
子問題來說,可以看成是已經空了的)放到C柱子上了,所以也是空的了)
所以問題變成把B柱子上的a-1個圓盤,經過輔助柱子A全都放到C柱子上。
因此我們可以調用遞歸
solve(B,A,C,a-1,0,0);
從而得到完整的程序:

void solve(char A,char B,char C,int a,int b,int c){
  if (a==0){
    說明沒有任何圓盤要移動,直接return;
  }
  solve(A,C,B,a-1,0,0);
  cout<<A<<"柱子上最頂端的圓盤直接放到"<<C<<"柱子上";
  solve(B,A,C,a-1,0,0);
}

會發現其實b,c柱子對於我們來說總是空的,所以我們不必要記錄他們的柱子的個數。
可以簡化一下,只用n來記錄A柱子上的圓盤的個數。

void solve(char A,char B,char C,int n){
  if (n==0) return;
  solve(A,C,B,n-1);
  cout<<A<<"->"<<C<<endl;
  solve(B,A,C,n-1);
}

主程序調用solve('A','B','C',n);

運行結果:(會發現操作步驟總是(2^n)-1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM