Hanoi塔


Hanoi塔問題是源於印度一個古老傳說的益智玩具。設a,b,c是三個塔座,開始時,在塔座a上有一疊共n個圓盤,這些圓盤自上而下,由大到小疊在一起,各圓盤的編號為1,2,3,...,n。現要求將塔座a上的這一疊圓盤移動到塔座b上,並仍按從到到小的順序疊置。再移動圓盤時應該遵守以下移動規則:

規則一:每次只能移動一個圓盤。

規則二:不允許將較大的圓盤壓在較小的圓盤上面。

規則三:在滿足規則一、規則二的情況下,可將圓盤移動到啊a,b,c中任一塔座上。

如圖為一Hanoi塔問題的移動步驟:

a

圓盤總數為n,當n=1時,只要將編號為1的圓盤從塔座a直接移動到塔座b。當n>1時,需要利用塔座c作為輔助,將n-1個較小的圓盤依照移動規則從塔座a移動至塔座c,然后將最大的圓盤從塔座a移動至塔座b,再將n-1個圓盤從塔座c移動至塔座b,T(n)=2^(n-1)-1。由於n-1個圓盤以同樣的方式移動了兩次,可以利用遞歸方法來解決。這個就是典型的Hanoi塔遞歸問題。

 public static void hanoi(int n,int a,int b,int c){
        if(n>0){
            hanoi(n-1,a,c,b);
            move(a,b);
            hanoi(n-1,c,b,a);
        }
    }

 

如果將三個塔座a,b,c改為四個塔座a,b,c,d,利用分治法則和歸納法分成兩個小規模問題。

T(n)來表示最小步數,可以知道T(1)=1,T(2)=3,當n>=3時,假設已經輸出T(1),T(2),…,T(n-1),則T(n)可以這樣計算:

for i=1;i<n;i++

一:先從塔座a移動i個圓盤到塔座d(b,c),塔座b,c作為輔助,移動步數為T(i)。

二:剩下的n-i個圓盤只可以在三個塔座之間移動,利用Hanoi函數將其由塔座a移動至塔座,塔座c作為輔助,移動步數為T(n-i)=2^(n-i)-1。

三:再將塔座d中的i個圓盤移動至塔座b,塔座c,a作為輔助(與步一移動方式相同)移動步數為T(i)。

public class Han {

    //創建一個靜態數組用於存儲移動的圓盤的最小步數
    static int[] a=new int [65];

    //創建一個靜態數組用於存儲先移動的圓盤的個數
    static int[] s=new int [65];

    public static void step() {
        a[0] = 0;
        a[1] = 1;
        a[2] = 3;

        //先移動圓盤個數的循環
        for (int n = 3; n <= 64; n++) {
            double min = 200000;
            double temp = 0;
            int temp2 = 0;

            //一個一個移動計算步數和圓盤個數
            for (int i = 1; i < n; i++) {
                temp = 2 * a[i] + Math.pow(2, n - i) - 1;
                if (temp < min) {
                    min = temp;//計算出移動總步數並賦值
                    temp2 = i;//計算出先移動圓盤數並賦值
                }
                a[n] = (int) temp;
                s[n] = temp2;
            }
        }
    }

 

public static void hanoi4(int n,int a,int b,int c,int d,int []s){
        if(n>=3){
            hanoi4(s[n],a,b,c,d,s);
            hanoi(n-s[n],a,c,b);
            hanoi4(s[n],d,a,b,c,s);
        }else{
            if(n==1){
                move(a,b);
            }
            if(n==2){
                hanoi(2,a,c,b);
            }
        }
    }

 

示例:

Hanoi塔問題中如果塔的個數變為a,b,c,d四個,現要將n個圓盤從a全部移動到d移動規則不變,編寫移動步數最小的方案和具體的移動過程(只考慮n<=64),並演示當n=9時的情形。

package jihe;

/**
 * author Gsan
 */
public class Han {

    //創建一個靜態數組用於存儲移動的圓盤的最小步數
    static int[] a=new int [65];

    //創建一個靜態數組用於存儲先移動的圓盤的個數
    static int[] s=new int [65];

    public static void step() {
        a[0] = 0;
        a[1] = 1;
        a[2] = 3;

        //先移動圓盤個數的循環
        for (int n = 3; n <= 64; n++) {
            double min = 200000;
            double temp = 0;
            int temp2 = 0;

            //一個一個移動計算步數和圓盤個數
            for (int i = 1; i < n; i++) {
                temp = 2 * a[i] + Math.pow(2, n - i) - 1;
                if (temp < min) {
                    min = temp;//計算出移動總步數並賦值
                    temp2 = i;//計算出先移動圓盤數並賦值
                }
                a[n] = (int) temp;
                s[n] = temp2;
            }
        }
    }

    public static void hanoi(int n,int a,int b,int c){
        if(n>0){
            hanoi(n-1,a,c,b);
            move(a,b);
            hanoi(n-1,c,b,a);
        }
    }

    public static void move(int a,int b){
        System.out.println("move"+a+"to"+b);
    }

    public static void hanoi4(int n,int a,int b,int c,int d,int []s){
        if(n>=3){
            hanoi4(s[n],a,b,c,d,s);
            hanoi(n-s[n],a,c,b);
            hanoi4(s[n],d,a,b,c,s);
        }else{
            if(n==1){
                move(a,b);
            }
            if(n==2){
                hanoi(2,a,c,b);
            }
        }
    }

    public static void main(String[] args) {

        int n=9;
        step();
        hanoi4(n,1,2,3,4,s);

        System.out.println("9個圓盤移動的最小步數為:"+a[n]);
    }

}

運行結果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM