算法學習-類似漢諾塔的問題

題目:
漢諾塔問題比較經典,這裏修改一下遊戲規則:
現在限制不能從最左側的塔直接移動到最右 側,也不能從最右側直接移動到最左側,而是必須經過中間。求當塔有 N 層的時候,打印
最優移動過程和最優移動總步數。
例如,當塔數爲兩層時,最上層的塔記爲 1,最下層的塔記爲 2,則打印:

Move 1 from left to mid
Move 1 from mid to right
Move 2 from left to mid
Move 1 from right to mid
Move 1 from mid to left
Move 2 from mid to right
Move 1 from left to mid
Move 1 from mid to right

It will move 8 steps.

【要求】
用以下兩種方法解決。
方法一:遞歸的方法;
方法二:非遞歸的方法,用棧來模擬漢諾塔的三個塔。

這個問題和經典的漢諾塔很像咯,漢諾塔是最經典的聯繫遞歸的算法了,這裏左神用遞歸和非遞歸(棧)兩種方式來實現了;

  1. 遞歸

    // 遞歸做
    public static int hanoiProblem1(int N,String left,String mid,String right){
        if(N<1) return 0;
    
        return process1(N,left,mid,right,left,right);
    }
    
    // from —> to
        public static int process1(int N,String left,String mid,String right,String from,String to){
        // 只有1層塔的情況
        if(N==1){
        // from/to存在mid的情況,直接一步就可完成from,to(遊戲規則,只可以藉助mid進行移動)
            if(from.equals(mid) || to.equals(mid)){
                System.out.println("move 1 from "+from+" to "+to);
                return 1;
            }else{
            // 移動不是在mid上進行,則需要藉助mid進行兩步操作
                System.out.println("move 1 from "+from+" to mid");
                System.out.println("move 1 from mid to "+to);
                return 2;
            }
        }
    
    // 其他情況(即 多層塔層疊)
    // 存在mid移動的情況,分三步
    if(from.equals(mid) || to.equals(mid)){
        // 3 steps
        // 現在要把塔從from->to上,因爲是多層塔,所以需要藉助另外的塔把壓在上面的塔移走,這樣最下面的塔才能移到to上啊~~~
        String another=from.equals(left) || to.equals(left)?right:left;
    
        int part1=process1(N-1,left,mid,right,from,another);
        System.out.println("move "+N+" from "+from+" to "+to);
        int part2=process1(N-1,left,mid,right,another,to);
        return part1+part2+1;
    }else{
        // 不是mid的情況,則需要藉助mid進行5步操作了
        // 5 steps
        int part1=process1(N-1,left,mid,right,from,to);
        System.out.println("move "+N+" from "+from+" to mid");
        int part2=process1(N-1,left,mid,right,to,from);
        System.out.println("move "+N+" from mid to "+to);
        int part3=process1(N-1,left,mid,right,from,to);
        return part1+part2+part3+2;
    }
    }
    

上面就是遞歸操作的算法了,主要是分情況進行分析,這裏要注意,很多人會問規則不是 只能從 mid 經過才能走嗎,爲什麼還有類似於

int part1=process1(N-1,left,mid,right,from,another);

這樣(可能直接 left->right的代碼呢),請注意,這是遞歸操作,我們只需要注意print的操作是完全符合 遊戲規則就可以了,遞歸操作進入再分析的時候就會符合遊戲規則啦~~

  1. 非遞歸操作(棧操作)
    對於棧操作的時候,需要有幾點說明:
    首先,對於移動漢諾塔,符合要求的操作只有4種,LToM,MToL,MToR,RToM;
    其次,由於要求的是最少步數,也就是如果前一步是LToM,則這步絕對不可能是MToL(這樣不就循環操作了嗎,怎麼可能是最優步數);
    最後,操作的規則,只能小壓大;

對了,還有一個小的trick,爲了使得初始化時都可以壓入數據,事先在棧中壓入最大值(不壓入的話,需要處理當棧爲空的情況);

鑑於上述規則,我們可以定義3個棧來進行操作,代碼如下:

public static enum Action {
        No, LToM, MToL, MToR, RToM
}

// 棧做
public static int hanoiProblem2(int N,String left,String mid,String right){
    if(N<1) return 0;
    Stack<Integer> fStack=new Stack<Integer>();
    Stack<Integer> mStack=new Stack<Integer>();
    Stack<Integer> tStack=new Stack<Integer>();
    fStack.push(Integer.MAX_VALUE);
    mStack.push(Integer.MAX_VALUE);
    tStack.push(Integer.MAX_VALUE);

    for(int i=N;i>=1;i--){
        fStack.push(i);
    }

    // record主要是記錄上一次的操作
    Action[] record={Action.No};
    int step=0;
    while(tStack.size()!=N+1){
        step+=process2(record,Action.MToL,Action.LToM,fStack,mStack,left,mid);
        step+=process2(record,Action.LToM,Action.MToL,mStack,fStack,mid,left);
        step+=process2(record,Action.RToM,Action.MToR,mStack,tStack,mid,right);
        step+=process2(record,Action.MToR,Action.RToM,tStack,mStack,right,mid);
    }

    return step;
}

public static int process2(Action[] record,Action preNoAct,Action curAct,Stack<Integer> fStack,
        Stack<Integer> tStack,String from,String to){
    // 當上一次的操作 爲當前上次不允許的操作時,返回
    // 當大壓小時,不符合操作規則,返回
    if(record[0]==preNoAct || fStack.peek()>=tStack.peek()){
        return 0;
    }

    // 本次操作允許,記錄
    tStack.push(fStack.pop());
    System.out.println("move "+tStack.peek()+" from "+from+" to "+to);
    record[0]=curAct;
    return 1;
}

以上就是這個問題的求解過程了,比較能練手,好評~~~

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