1. 程式人生 > >四柱加強版漢諾塔HanoiTower----是甜蜜還是煩惱

四柱加強版漢諾塔HanoiTower----是甜蜜還是煩惱

我想很多人第一次學習遞迴的時候,老師或者書本上可能會舉漢諾塔的例子。

但是今天,我們討論的重點不是簡單的漢諾塔演算法,而是三柱漢諾塔的延伸。先來看看經典的三柱漢諾塔。

一、三柱漢諾塔(Hanoi_Three):

我想大家對於三柱漢諾塔的理解以及演算法的實現應該是很熟練了。

我在這裡簡單的過一遍三柱漢諾塔的演算法思想:

有A、B、C三根柱子,A柱上有n個盤子,現在需要將A上所有的盤子轉移到C上,請給出搬運次數最少的步驟。

演算法思想:

1、將A上n-1個盤子以C為快取,全部轉移到 B 柱上。

2、將A上留下的第n個盤子,直接轉移到 C  柱上。

3、將B上的n-1個盤子,以A為快取,全部轉移到 C 柱上。

很容易得到演算法的遞迴方程為:T(n)=2*T(n-1)+1,不難算出步數是T(n)=2^n-1

具體的程式碼如下:

  1. void Move( char x, char y )  
  2. {  
  3.     printf("%c --> %c\n", x, y); //列印路徑,x-->y
  4. }  
  5. void Hanoi_Three( int n, char a, char b, char c )  
  6. {  
  7.     if( n <= 0 )  
  8.         return ;      
  9.     step++;     //步驟+1
  10.     if( n == 1 )  
  11.     {  
  12.         Move( a, c );   //將a柱上的一個盤子直接移動到c柱
  13.         return ;      
  14.     }  
  15.     else
  16.     {  
  17.         Hanoi_Three( n-1, a, c, b);     //將a上n-1個盤子以c為快取,全部轉移到 b 柱上
  18.         Move( a, c );                   //將A上留下的第n個盤子,直接轉移到 C  柱上
  19.         Hanoi_Three( n-1, b, a, c);     //將b上的n-1個盤子,以a為快取,全部轉移到 c 柱上
  20.     }  
  21. }  


二、四柱漢諾塔(Hanoi_Four):

當柱子為四根時,對於只是將盤子全部轉移到另一根柱子上這個目的來說,是大大降低了難度,而且演算法的複雜度也大大降低了。但是,這個時候,如果要你找到一個最優的、步驟最少的實現方法,可以說難度是提升了一個數量級。

有些人可能會質疑,為什麼,我用三柱漢諾塔的思想不是很優化了嗎?

別急,且讓我慢慢向你道來。

先來看看這種‘看上去很合理’的解法:

假設,A,B,C,D,分別為:源位置,快取,快取,目的位置。

因為三柱的時候,我們是將A的前n-1個盤子放到B上快取,然後將第n個盤子放到C柱上。

現在的情況好很多,有兩個可以快取的柱子,因此,看上去移動起來更加方便,原來B上需要快取的n-1個盤子,現在可以只是n-2個盤子,而將第n-1個盤子放到C上快取。

具體的流程如下(非最優解法):

1、從A藉助C、D將 n-2個盤子移動到B上。

2、將第n-1個盤子移動到C上。

3、將第n個盤子移動到D上。

4、將第n-1個盤子移動到D上。

5、從B藉助A、C將 n-2個盤子全部移動到D上。

看上去,非常完美,筆者也一度覺得這個思想沒有破綻,甚至我還自以為找到了k根柱子漢諾塔的通用方法(想當然的將B柱上快取的數量從n-2個,改為n-(k-2)個盤子)。直到我看了這篇文章:

    雖然我們想到讓盤子儘量不發生重疊來保證步數的最少,但是這並不能絕對保證。或許在盤子較少的情況下是可行的,但是盤子增多時,那些多餘的只有一個盤子的柱子是可以加以利用的(可能的優化在這裡)。雖然這麼做加多了每次的移動步數,但是卻從另一個側面減少了遞迴的數量,因此我們需要從這裡邊找一個平衡點。

下面我們來看看,1941年,美國的J. S. Frame,給出的四柱漢諾塔的演算法思想,也叫Frame演算法

1、用4柱漢諾塔演算法把A柱上部分的n- r個碟子通過C柱和D柱移到B柱上【F( n- r )步】。

2、用3柱漢諾塔經典演算法把A柱上剩餘的r個碟子通過C柱移到D柱上【2^r-1步】(參照上述三柱時的情況)。

3、用4柱漢諾塔演算法把B柱上的n-r個碟子通過A柱和C柱移到D柱上【F(n-r)步】。

4、依據上邊規則求出所有r(1≤r≤n)情況下步數f(n),取最小值得最終解。

因此Frame演算法的遞迴方程如下:

F(n)=min(2*F(n-r)+2^r-1),(1≤r≤n)。

大家有沒有發現,其實,這個演算法思想跟我們之前認為合理的演算法基本一致,差別只是在於他將我們的n-2個碟子快取到B上,改為了將n- r個碟子轉移到B柱上。

差別即使核心,這個演算法的核心,就是計算n個盤子的情況下,r為何值時,能夠使得演算法最優。

找到了核心,我們現在的任務就明確了,就是對r值的計算。

這裡給出了一個較笨的方法--列舉(不知道各位有沒有其他方法)。就是將一定範圍內的n與r的所有取值帶入,得到滿足F(n)為最小值的r的值,記為K[n] = r;

具體的程式碼如下:

  1. void Init_K(void )  
  2. {  
  3.     int i, k;     
  4.     __int64 temp;     
  5.     __int64 m[Max+1] = {0};       
  6.     for( i = 1; i <= Max; i++ )  
  7.     {  
  8.         m[i] = INT_MAX;       
  9.         for( k = 1; k <= i; k++ )  
  10.         {  
  11.             temp = 2*m[i-k] + (__int64)pow(2,k) - 1;      
  12.             if( temp < m[i] )  
  13.             {  
  14.                 m[i] = temp;      
  15.                 K[i] = k;     
  16.                 //printf("K[%d] = %d, m[i] = %d\n", i, k, temp );   
  17.             }  
  18.         }  
  19.     }  
  20. }  


得到各個n對於的r之後,演算法將變的非常簡單,具體實現如下:

  1. #include <stdio.h>
  2. #include <math.h>
  3. #define Max 100 
  4. #define INT_MAX 0xfffffffffffffff
  5. int K[Max+1] = {0};   
  6. int step = 0;     
  7. void Hanoi_Four( int n, char a, char b, char c, char d );     
  8. void Hanoi_Three( int n, char a, char b, char c );    
  9. void Move( char x, char y );      
  10. void Move( char x, char y )  
  11. {  
  12.     printf("%c --> %c\n", x, y); //列印路徑,x-->y
  13. }  
  14. void Hanoi_Three( int n, char a, char b, char c )  
  15. {  
  16.     if( n <= 0 )  
  17.         return ;      
  18.     step++;     //步驟+1
  19.     if( n == 1 )  
  20.     {  
  21.         Move( a, c );   //將a柱上的一個盤子直接移動到c柱
  22.         return ;      
  23.     }  
  24.     else
  25.     {  
  26.         Hanoi_Three( n-1, a, c, b);     //將a上n-1個盤子以c為快取,全部轉移到 b 柱上
  27.         Move( a, c );                   //將A上留下的第n個盤子,直接轉移到 C  柱上
  28.         Hanoi_Three( n-1, b, a, c);     //將b上的n-1個盤子,以a為快取,全部轉移到 c 柱上
  29.     }  
  30. }  
  31. void Hanoi_Four( int n, char a, char b, char c, char d )  
  32. {  
  33.     if( n <= 0 )  
  34.         return ;      
  35.     if( n == 1 )  
  36.     {  
  37.         step++;   
  38.         Move( a, d );     
  39.         return ;      
  40.     }  
  41.     else
  42.     {  
  43.         int kn = K[n];    
  44.         //printf("kn = %d\n", K[n]);    
  45.         Hanoi_Four( n-kn, a, c, d, b );     //用4柱漢諾塔演算法把A柱上部分的n- kn個碟子通過C柱和D柱移到B柱上
  46.         Hanoi_Three( kn, a, c, d );         //用3柱漢諾塔經典演算法把A柱上剩餘的kn個碟子通過C柱移到D柱上。
  47.         Hanoi_Four( n-kn, b, a, c, d );     //用4柱漢諾塔演算法把B柱上的n-r個碟子通過A柱和C柱移到D柱上
  48.     }  
  49. }  
  50. void Init_K(void )  
  51. {  
  52.     int i, k;     
  53.     __int64 temp;     
  54.     __int64 m[Max+1] = {0};       
  55.     for( i = 1; i <= Max; i++ )  
  56.     {  
  57.         m[i] = INT_MAX;       
  58.         for( k = 1; k <= i; k++ )  
  59.         {  
  60.             temp = 2*m[i-k] + (__int64)pow(2,k) - 1;      
  61.             if( temp < m[i] )  
  62.             {  
  63.                 m[i] = temp;      
  64.                 K[i] = k;     
  65.                 //printf("K[%d] = %d, m[i] = %d\n", i, k, temp );   
  66.             }  
  67.         }  
  68.     }  
  69. }  
  70. int main()  
  71. {  
  72.     int n;    
  73.     Init_K();     
  74.     printf("Please enter the number of the Plates: \n");  
  75.     while( scanf("%d", &n) != EOF )  
  76.     {  
  77.         step = 0;     
  78.         Hanoi_Four( n, 'A''B''C''D' );      
  79.         //Hanoi_Three( n, 'A', 'B', 'C' );  
  80.         printf("**************************\nTotal Step: %d\n", step );    
  81.         printf("Please enter the number of the Plates: \n");  
  82.     }  
  83.     return 0;     
  84. }  

相關推薦

加強HanoiTower----是甜蜜還是煩惱

我想很多人第一次學習遞迴的時候,老師或者書本上可能會舉漢諾塔的例子。 但是今天,我們討論的重點不是簡單的漢諾塔演算法,而是三柱漢諾塔的延伸。先來看看經典的三柱漢諾塔。 一、三柱漢諾塔(Hanoi_Three): 我想大家對於三柱漢諾塔的理解以及演算法的實現應該是很

檔案操作類問題

題目描述: 有A、B、C三個盤子用來盛餅,餅的個頭有大有小,沒有大小完全相同的,餅在盤子中必須大個的在下面,小個的放在上面。現在 A 盤中放著 n 張薄餅(n由檔案input02.txt讀入),需要藉助 B 盤放在 C&nbs

根柱子處理問題的最少次數

經典的漢諾塔問題經常作為一個遞迴的經典例題存在。可能有人並不知道漢諾塔問題的典故。漢諾塔來源於印度傳說的一個故事,上帝創造世界時作了三根金剛石柱子,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。上帝命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。

動規之問題

四柱漢諾塔問題 首先我們先回憶一下經典的漢諾塔問題: 問題描述 相傳在古印度聖廟中,有一種被稱為漢諾塔(Hanoi)的遊戲。該遊戲是在一塊銅板裝置上,有三根杆(編號A、B、C),在A杆自下而上、由大到小按順序放置64個金盤(如下圖)。遊戲的目標:把A杆上的金盤全部移到C杆上,並仍保持

hdu1207 優化後的,

#include<stdio.h> #include<math.h> #define m 99999999//其實定義在裡面也可以,,定義在這因為數太大 int main() {         int n,i,min,j;         int

hdu1207II

Problem Description 經典的漢諾塔問題經常作為一個遞迴的經典例題存在。可能有人並不知道漢諾塔問題的典故。漢諾塔來源於印度傳說的一個故事,上帝創造世界時作了三根金剛石柱子,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。上帝命令婆羅門把

uva 10254()

題意:給出四柱漢諾塔上初始柱子上圓盤的數量n,問最優多少次移動可以移動到另一個柱子上。 題解:可以參考四柱漢諾塔之初步探究和四柱漢諾塔實現這兩篇論文,理解四柱漢諾塔的原理。 但是這道題n是從1到1

nyoj 1078 )[二分圖 || 規律 || 暴力 || 貪心]

二分圖 二分圖匹配 int 處理 names 特殊 mes while 最小路徑覆蓋 題目:nyoj 1078 漢諾塔(四) 分析:做這個題目的時候是在圖論的題目裏面看到的。到時讀了題目推了一下,發現好像有點規律。試了一下果然過了。 後來看了一下數據,才50。那

習題3.10 的非遞歸實現(25 分)浙大《數據結構(第2)》題目集

-i pro 數據結構 但是 int 遞歸實現 記錄 表達 names 借助堆棧以非遞歸(循環)方式求解漢諾塔的問題(n, a, b, c),即將N個盤子從起始柱(標記為“a”)通過借助柱(標記為“b”)移動到目標柱(

演算法之路()----(又稱河內之

漢諾塔是很簡單也很經典的演算法之一。 漢諾塔是根據一個傳說形成的數學問題: 有三根杆子A,B,C 。A杆上有N個(N>1)穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至C杆: * 1 每次只能移動一個圓盤; * 2 大盤不能疊在小盤上面。 提示:可將圓

我理解的---java

漢諾塔(摘自百度百科):         漢諾塔(又稱河內塔)問題是源於印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上

1.0(普通次數

好吧,作為資訊統練中的基礎題,在網上確實找不到,因此我找了一道需要高精度的題進行改編 漢諾塔 時空限制1000ms / 128MB 題目背景 直達通天路·小A歷險記第四篇 題目描述

c++實現

小的盤子必須在大的盤子的上面 void hannoi(int n, char a, char b, char c ) {     if (n == 1)     {         res.push_back(to_string(n)+" from " +a+" to "+

【演算法】 Python

題目: 漢諾塔給出最優解,如果對漢諾塔的定義有不瞭解,請翻看資料結構教材。 除了最基本的之外,還有一題,給定一個數組,arr=[2,3,1,2,3],其含義是這是一個有5個圓盤的漢諾塔,每一個數字代表這個圓盤所在的位置,1代表左邊的柱子,2代表中間,3代表

資料結構Java實現——①棧-->棧的應用問題

寫在前面 只是學棧的描述之類的似乎很無聊,所以我特意找了幾個比較有意思的例子,一則加深對棧的理解和運用,二則,也可以開拓一下思路,此為例四 例四、漢諾塔 1、問題描述 漢諾塔:漢諾塔(又

7-17 的非遞迴實現(25 分)(附:遞迴

題目大意:略。 解題思路:如果考慮一下把64片金盤,由一根柱子上移到另一根柱子上,並且始終保持上小下大的順序。這需要多少次移動呢?這裡需要遞迴的方法。假設有n片,移動最少次數是f(n).顯

問題(python

漢諾塔的移動可以用遞迴函式非常簡單地實現。(此題可以聯絡 棧的反轉 問題) 請編寫move(n, a, b, c)函式,它接收引數n,表示3個柱子A、B、C中第1個柱子A的盤子數量,然後打印出把所有盤子從A藉助B移動到C的方法,例如: 演算法:當只有一個盤子的時候

移動

pri -- nbsp else == move 漢諾塔 int bsp 學習python進行中: def move(n, a, b, c): if n ==1: print a,‘-->‘,c else: move(n-1,a,c

學習筆記,有不正確的地方請小夥伴們指正~·~

學習 順序執行 == cab -1 nbsp 什麽 猜想 abc 1* n=3.abc; 2* n-1=2,acb; 3* n-1=1,abc 1* n=3,執行hanoi(n-1,A,C,B); =>2* n-1=2,acb執行hanoi

(河內)問題:

漢諾塔 medium 問題 http int logs 一行 移動 else     漢諾塔(又稱河內塔)問題是源於印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小