【題目描述】
約19世紀末,在歐州的商店中出售一種智力玩具,在一塊銅板上有三根桿,最左邊的桿上自上而下、由小到大順序串着由64個圓盤構成的塔。目的是將最左邊桿上的盤全部移到中間的桿上,條件是一次只能移動一個盤,且不允許大盤放在小盤的上面。
這是一個著名的問題,幾乎所有的教材上都有這個問題。由於條件是一次只能移動一個盤,且不允許大盤放在小盤上面,所以64個盤的移動次數是:18,446,744,073,709,551,615
這是一個天文數字,若每一微秒可能計算(並不輸出)一次移動,那么也需要幾乎一百萬年。我們僅能找出問題的解決方法並解決較小N值時的漢諾塔,但很難用計算機解決64層的漢諾塔。
假定圓盤從小到大編號為1, 2, ...
【輸入】
輸入為一個整數(小於20)后面跟三個單字符字符串。
整數為盤子的數目,后三個字符表示三個桿子的編號。
【輸出】
輸出每一步移動盤子的記錄。一次移動一行。
每次移動的記錄為例如 a->3->b 的形式,即把編號為3的盤子從a桿移至b桿。
【輸入樣例】
2 a b c
【輸出樣例】
a->1->c a->2->b c->1->b
例題不怎么詳的解:
其實這一題搞半天了還是沒什么吃透。總感覺哪里怪怪的。但是如果硬要說,這題無論是思路還是代碼實現都很簡單。
但是總感覺有點。。。我居然還去7k7k玩漢諾塔小游戲。。。怪難理解的這遞歸。
幾乎所有解析都是一個思路,例子也是一樣的。64個盤子,拿63個當整體的都看到無數遍了,但是總感覺這樣解釋有點。。。
據我分析,無論多少個盤子,實際上都分為三個步驟(設共有n個盤子,a是起始柱,b是中轉柱,c是目標柱):
1、將n-1個盤子移至b柱;
2、將第n個盤子移到c柱(需要一步);
3、將n-1個盤子移到第n個盤子上面;
第一步又可以分解為依次將一個盤子,兩個盤子,三個盤子,四個···移動到b柱上,巧的是,移動x個盤子到b柱上所需步數恰好是僅有x個盤子時達到目標所需步驟。
第二步多出來的那一步就是將第n個盤子挪到c那一步;
第三步和第一步一樣,把n-1個盤子從b挪到c,也就是第n個盤子上;
顯而易見,若當前盤子為x個,那么所需步驟為(移動x-1個盤子所需步驟)*2+1步(這里是為了遞歸好理解,當然也可以理解做2^n-1);
當然,這只是基本規律,在不清楚算法之前,首先理解了這一點,相當於把握了這道題的關鍵,接下來的遞歸操作,得到每一步的走法,其實不過是將基礎的步驟重復幾遍。
廢話不多說,暴力的規律在這里:
1——1
2——3
3——7
4——15
5——31
6——63
7——127
我一看,規律明擺着在這里嘛!
好的,有句老話說得好,無圖說個***,接下來,上圖(以四個盤子為例)!
第一步:

我雖然有些顧慮,就是這一坨滿滿變高的塔每增高一格就會跑到另一根柱子上去。
但是事實證明,最后的遞歸算法很“聰明”,巧妙轉換了每一次塔的位置,將塔在b,c兩柱之間對調。
第二步:
看到沒,把第n個盤子從a移到b,走了一步;
第三步:
其實是重復第一步!!!
到最后完成!
到這里,我希望也相信大家包括我自己都可以對解漢諾塔的原理有個清晰完整的了解,而不是像別的解析一樣上來就講算法。
算法部分:
重頭戲來了。
這個算法就比較神奇了。
具體怎么神奇呢?——本算法沒有任何需要儲存的東西,除了調用棧,這貨就是把移動方式給你打印一遍,實際上自己也不知道自己在干什么。
所以!很多初學者,包括我,也在這里繞暈了。
慢慢來,先是第一步,從2個盤子看起,顯而易見,最快的方法是將最上面的第1個盤子放在中轉柱上,接下來將第2個盤子放在目標柱上,最后將第1個盤子放到目標柱上,共三步。別小看了這三步!這可是歷史性的最基礎的三步!為啥?前面講到,若當前盤子為x個,那么所需步驟為(移動x-1個盤子所需步驟)*2+1步;
恍然大悟!得出以(n-1)作為遞歸式,(n=0或1)作為遞歸邊界。。。好吧這很明顯。(雖然兩個遞歸邊界得出的是兩種不同的算法)
重點在下面的移動上,這里肯定倒了一片人,當然還有我。
牢記!!!
先將最上面的盤子從起始柱a柱移到中轉柱,也就是b柱上。即 a->1->b;
接下來,將第2個盤子放在目標柱上。即 a->2->c;
最后,將第1個盤子放到c柱上。 即 b->1->c;
完成!這樣就明白多了!(其實我寫到這里時還是一臉懵逼。。。)
由此得出遞歸式,Hanoi(n-1,a,b,c),輸出a->n->b,表示把a上第n個盤子移到b上,將c柱作為中轉柱;Hanoi(n-1,b,c,a),表示把b柱上的第n個盤子移到c柱上,將a柱作為中轉柱。
很疑惑啊對不對!中間的第二步呢!?其實,它“藏”在第三步里,我們在寫程序時,將第三步寫在最后,就達到了效果。
void mov(int n,char a,char c,char b) { if(n==0) return; mov(n-1,a,b,c); cout<<a<<"-->"<<n<<"-->"<<c<<endl; mov(n-1,b,c,a); }
好吧說實話我也不是很懂。。。
樣例代碼:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int n; char x,y,z; void mov(int n,char a,char c,char b) { if(n==0) return; mov(n-1,a,b,c); cout<<a<<"-->"<<n<<"-->"<<c<<endl; mov(n-1,b,c,a); } int main() { cin>>n; cin>>x>>y>>z; mov(n,x,y,z); return 0; }
看起來很簡單把。
2019-01-30 22:52:48