1. 程式人生 > >漢諾塔問題(+遞推公式)

漢諾塔問題(+遞推公式)



漢諾塔問題是使用遞迴解決問題的經典範例。

  漢諾(Hanoi)塔問題:古代有一個梵塔,塔內有三個座A、B、C,A座上有64個盤子,盤子大小不等,大的在下,小的在上(如圖)。有一個和尚想把這64個盤子從A座移到B座,但每次只能允許移動一個盤子,並且在移動過程中,3個座上的盤子始終保持大盤在下,小盤在上。在移動過程中可以利用B座,要求列印移動的步驟。如果只有一個盤子,則不需要利用B座,直接將盤子從A移動到C。

  • 如果有2個盤子,可以先將盤子1上的盤子2移動到B;將盤子1移動到c;將盤子2移動到c。這說明了:可以藉助B將2個盤子從A移動到C,當然,也可以藉助C將2個盤子從A移動到B。
  • 如果有3個盤子,那麼根據2個盤子的結論,可以藉助c將盤子1上的兩個盤子從A移動到B;將盤子1從A移動到C,A變成空座;藉助A座,將B上的兩個盤子移動到C。這說明:可以藉助一個空座,將3個盤子從一個座移動到另一個。
  • 如果有4個盤子,那麼首先借助空座C,將盤子1上的三個盤子從A移動到B;將盤子1移動到C,A變成空座;藉助空座A,將B座上的三個盤子移動到C。
  • 程式碼如下:
  • #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<limits.h>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<math.h>
    #define maxn 1000005
    int step;
    void move(int a,int b)
    {
    	printf("move from %d to %d \n",a,b);
    }
    void hanoi(int n,int a,int b,int c)//a表示起點,b表示過渡的柱子,c表示終點 
    {
    	if(n==1)
    	{
        	++step;
        	move(a,c);
        }
    	else
    	{
    		++step;
    		move(a,c);
    		hanoi(n-1,a,c,b);
    		hanoi(n-1,b,a,c);
    	} 
    	
    }
    int  main()
    {
    	int T,i,j,n;
    	scanf("%d",&T);
    	while(T--)
    	{
    		step=0;
    		scanf("%d",&n);
    		hanoi(n,1,2,3);
    		printf("Total steps is %d\n",step);
    	}
    }
    拓展:遞推公式:f(n)=f(n-1)*2+1;
  • 拓展題目如下:
  • 漢諾塔(二)

    時間限制:3000ms  |  記憶體限制:65535KB 難度:5
    描述

    漢諾塔的規則這裡就不再多說了,詳見題目:漢諾塔(一)

    現在假設規定要把所有的金片移動到第三個針上,給你任意一種處於合法狀態的漢諾塔,你能計算出從當前狀態移動到目標狀態所需要的最少步數嗎?

    輸入
    第一行輸入一個整數N,表示測試資料的組數(0<N<20)
    每組測試資料的第一行是一個整數m表示漢諾塔的層數(0<m<32),隨後的一行有m個整數Ai,表示第i小的金片所在的針的編號。(三根針的編號分別為1,2,3)
    輸出
    輸出從當前狀態所所有的金片都移動到編號為3的針上所需要的最少總數
    樣例輸入
    2
    3
    1 1 1
    3
    1 1 3
    樣例輸出
    7
    3
    程式碼如下:
    #include<stdio.h>  
    #include<string.h>  
    int step[33],num[33];  
    void find()  
    {  
        step[0]=1;  
        for(int i=1;i<=33;i++)  
            step[i]=step[i-1]*2;  
    }  
    int main()  
    {  
        find();  
        int t,n,m;  
        scanf("%d",&t);  
        while(t--)  
        {  
            int t=3,i,sum=0;  
            scanf("%d",&n);  
            for(i=0;i<n;i++)  
                scanf("%d",&num[i]);  
            for(i=n-1;i>=0;i--)  
            {  
                if(num[i]!=t)  
                {  
                    sum+=step[i];  
                    t=6-num[i]-t;//當n-1時,如果第n-1小的(也就是最大的)不在3號針上,比如在2上,則會把小於第n-1個盤的其他盤移到1號針上去  
                                    //然後判斷第n-2小的盤在不在1號針上,依次類推......也就是針的總和數6減去num[i]所在的號數,減去n-1個盤所在的位置(自己模擬畫畫圖)  
                }  
            }  
            printf("%d\n",sum);  
        }  
    }