1. 程式人生 > 其它 >C語言用遞迴解決漢諾塔問題

C語言用遞迴解決漢諾塔問題

漢諾塔問題,來源於古印度的傳說。

在世界中心貝拿勒斯(在印度北部)的聖廟裡,一塊黃銅板上插著三根寶石針。印度教的主神梵天在創造世界的時候,在其中一根針上從下到上地穿好了由大到小的64片金片,這就是所謂的漢諾塔。不論白天黑夜,總有一個僧侶在按照下面的法則移動這些金片:一次只移動一片,不管在哪根針上,小片必須在大片上面。僧侶們預言,當所有的金片都從梵天穿好的那根針上移到另外一根針上時,世界就將在一聲霹靂中消滅,而梵塔、廟宇和眾生也都將同歸於盡。

那麼,我們什麼時候回迎來世界末日呢?

試試看!↓

題目內容:
漢諾塔是由三根杆子A,B,C組成的。A杆上有N個(N>0)穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至C杆:

  1. 每次只能移動一個圓盤;
  2. 大盤不能疊在小盤上面;
  3. 可將圓盤臨時置於B杆,也可將從A杆移出的圓盤重新移回A杆,但都必須尊循上述兩條規則。

輸入、輸出格式:
輸入圓盤數N,給出移動步驟。

輸入樣例1:
1
輸出樣例:
Step1: Move Disk1 from A to C

輸入樣例2:
2
輸出樣例:
Step1: Move Disk1 from A to B
Step2: Move Disk2 from A to C
Step3: Move Disk1 from B to C

輸入樣例3:
3
輸出樣例:
Step1: Move Disk1 from A to C
Step2: Move Disk2 from A to B

Step3: Move Disk1 from C to B
Step4: Move Disk3 from A to C
Step5: Move Disk1 from B to A
Step6: Move Disk2 from B to C
Step7: Move Disk1 from A to C

最初一看,哇…似乎是一個棘手的問題……

其實,在移動漢諾塔過程中,我們一直在重複一些動作。

如果我們一個盤一個盤去考慮,永遠考慮不出個東西來。那麼,這個時候,我們就需要類似整體法的東西。

經過筆頭計算和思考,我們可以發現:

我們要把10個盤子從A移動到C的時候,需要先將9個盤子從A移動到B,
然後把第10個盤子從A移動到C,
最後把這9個盤子從B移動到C。

我們要把9個盤子從A移動到B的時候,需要先將8個盤子從A移動到C,
然後把第9個盤子從A移動到B,
最後把這8個盤子從C移動到B。

總結一下:

我們要把n個盤子從原始杆移動到目的杆的時候,
需要先將n-1個盤子從原始杆移動到閒置杆
然後把第n個盤子從原始杆移動到目的杆
最後把這n-1個盤子從閒置杆移動到目的杆

然後我們就可以開始寫我們的move函式。

void move(int n, int num, char ini, char mid, char aim);
move函式需要接收5個引數:
n:本次移動需要移動的盤子數量;
num:移動盤子的時候盤子的編號;
ini:初始杆號;
mid:閒置杆號;
aim:目的杆號;

進行遞迴的時候,先要考慮邊界條件:

當只用移動1個盤子的時候輸出一次結果
if(n==1){
		step++;
        printf("Step%d: Move Disk%d from %c to %c\n",step,num,ini,aim);
    }
順便,為了記錄步驟數,每print一次,step++;

當不是移動1個盤子的時候,就完成盤子的移動,即:在這裡插入圖片描述
那麼,我們需要呼叫3次move函式:

else{
        move(n-1,num,ini,aim,mid);
        move(1,n,ini,mid,aim);
        move(n-1,num,mid,ini,aim);
    }

第一個move負責把n-1個盤子從原始杆經過目的杆移動到閒置杆;
第二個move負責把第n個盤子從原始杆移動到目的杆;
第三個move負責把n-1個盤子從閒置杆經過原始杆移動到目的杆;

注意,move函式自己呼叫了自己。
在第一個和第三個move中,當n-1不為1的時候,是不會有值輸出的,其實第二個值引數是沒有作用的,僅僅只有在n-1==1的時候會輸出盤子的編號,而我們再想想,由於第二個move輸出了n>=2的情況的盤子編號,那麼第一個和第三個move會輸出最開始最頂端的盤子編號,即“1”(在main中命名為disk)。

另外,在描述第二個move的時候,並沒有描述成“第二個move負責把第n個盤子從原始杆經過閒置杆移動到目的杆;”。因為本來就是直接移動的嘛,但是寫成函式必須要一個引數,就填成閒置杆的引數了,但其實是沒必要的。

最終,我們的程式就寫出來了:

#include<stdio.h>

void move(int n, int num, char ini,char mid, char aim);
int step;
char a='A',b='B',c='C';

int main(int argc, const char* argv[]){
    int n;
    int disk=1;
    scanf("%d",&n);
    move(n,disk,a,b,c);
    return 0;
}

void move(int n, int num, char ini, char mid, char aim){
    if(n==1){
        step++;
        printf("Step%d: Move Disk%d from %c to %c\n",step,num,ini,aim);
    }
    else{
        move(n-1,num,ini,aim,mid);
        move(1,n,ini,mid,aim);
        move(n-1,num,mid,ini,aim);
    }
}

好了,我們可以看一下傳說。

其實,我們可以發現,需要的步驟是2n-1次。
emm…264-1…………

print(2**64-1)

很好。是18446744073709551615次,嘿嘿。
來,我們來跑一下程式,

這是跑了1分鐘之後的輸出情況……

所以,你覺得我們的世界多久會毀滅呢?