Hanoi 漢諾塔——通俗易懂地講解(c++)

通俗易懂的解釋~

遊戲規則:

在這裏插入圖片描述

有A,B,C三根針,將A針上N個從小到大疊放的盤子移動到C針,一次只能移動一個,不重複移動,小盤子必須在大盤子上面。

問題:

總的移動次數是多少?

分析:

首先明確,我們的目標是將A針上所有N個盤子移動至C針。而對於B針,我們可以將之看成一箇中轉站。

這個問題,順向思維或者逆向思維道理是相同的,都太麻煩。我們不妨從中間開始思考

||: 規則要求小盤子必須在大盤子之上。試想這個過程中,必然會經歷那麼一個步驟,即有一大坨N-1個盤子在B針這個中轉站,而我們正將最大那個盤子(即第N個盤子)從A針移動至C針。

在這裏插入圖片描述只有經歷“移動最大盤子”這個步驟,餘下的事情纔有可能實現。而在此之前,我們所要做的事情,就是讓“移動最大盤子”這個步驟得以實現。

現在,遊戲整個過程以“移動最大盤子”爲中央,被分爲了兩部分。即(前)“將那坨N-1個盤子從A針移動到B針”,(中)“移動最大盤子”,(後)“將坨N-1個盤子從B針移動到C針”。

這是我們意識到,(前)與(後)操作道理是相似的。不去管那個最大盤子,(前)是以C針爲中轉站,(後)是以A針爲中轉站。因此兩者所需的移動次數應當是相等的。這意味着我們只要計算出其中一者的移動次數,然而乘以2,在加上“移動最大盤子”的那1次,就是這場遊戲的總移動次數了。

用數學語言表達,假設(前)“將N-1個盤子從A針移動到B針”所需次數爲Hn-1,總移動次數爲Hn,那麼可以得出的關係就是:Hn=(Hn-1)x 2 + 1.

其實當我們得出這個算式的時候,稍微聰明一點的人已經明白,這就是一個遞推公式,可以直接用此公式得出Hn的通解。

但是LZ比較笨,就是不明白,爲什麼這個公式就可以套用呢?

那麼就乾脆繼續思考吧。

讓我們再想象一個情景:最大那個盤子在剛剛從A針被移動到C針,而那坨N-1個盤子還在B針蠢蠢欲動地等待着,即處於(中)->(後)的這個狀態。

怎麼移動這N-1個盤子呢?

其實這時候,問題已經回到了筆者標示“||:”符號的地方。“||:”是樂譜中的反覆記號,而我們要做的,就是重複上面的步驟,但是要將N替換爲N-1,因爲現在只剩下N-1個盤子需要移動。而中轉站則從B變成了A(鑑於這時盤子都在B針)。目標仍然是C針。下一次重複的時候,只剩下N-2個盤子需要移動,中轉站又回到B,目標不變仍然是C針。……整個過程中,變化的只是中轉站(在A與B之間輪換),以及剩下那些所需要移動的盤子的總數(越來越少)而已。

那麼那個大盤子怎麼辦?不去管它嗎??

正解!!

因爲你已經把它移到C針,已經完成了這個移動步驟,它不會影響之後的操作。提醒自己牢記遊戲規則,大盤子永遠在小盤子下面,而你也不需要再重複移動它——“不重複移動”,正是遊戲規則的要求!

於是
Hn=Hn-1 x 2 + 1 這個公式,就可以套用、套用、套用……直到H3=7,H2=3,H1=1。

最後,用最懶的數學歸納法證明通項公式
Hn = 2^n - 1 吧!沒辦法,LZ就是比較懶嘛~

代碼實例

//漢諾塔
#include <stdio.h>

void hannuota(int n, char A, char B, char C)
{
    /*
    如果是一個盤子,直接將盤子將a移到c
    否則先將a柱子上面的n-1個盤子藉助c移到b,
    然後直接將a柱子上的盤子從A移到c,
    最後將b柱子上的n-1個盤子藉助a移到c
    */
    if(1 == n)
    {
        printf("將編號爲%d的盤子直接從%c柱子移到%c柱子\n",n, A, C);
    }
    else
    {
        hannuota(n-1, A , C , B);
        printf("將編號爲%d的盤子直接從%c柱子移到%c柱子\n",n, A, C);
        hannuota(n-1, B, A, C);
    }
}
int main(void)
{
    char ch1 = 'A';
    char ch2 = 'B';
    char ch3 = 'B';

    int n;

    printf("請輸入要移動盤子的個數:");
    scanf("%d", &n);

    hannuota(n, 'A', 'B', 'C');

    return 0;
}

參考自:
YIHE陳

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章