更改漢諾塔移動規則後遞歸求解

更改漢諾塔移動規則後遞歸求解

原問題描述
有三根杆左中右( left,mid , right ),在其中一杆( from )自下而上、由大到小按順序放置num個圓盤。遊戲的目標:把該杆的圓盤( from )全部移到另一杆 (to) 上,並仍保持原有順序疊好。
操作規則:每次只能移動一個盤子,並且在移動過程中三根杆上都始終保持大盤在下,小盤在上。
原問題解答點擊這裏(沒有接觸過漢諾塔問題的同學建議先看看原題求解)
約束移動規則
現在限制不能從最左側的塔直接移動到最右側,也不能從最右側直接移動到最左側,而是必須經過中間。求當塔有N層的時候,打印最優移動過程和最優移動總步數。
舉例
當圓盤有兩個時,從 left 杆移到 right 杆,最上層的記爲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.
遞歸方法
大致思想就是要移動N個圓盤,先移動上邊N-1個圓盤,再移動剩下的那一個,整個過程交給遞歸。
遞歸頭(結束遞歸):
首先,如果只剩最上層的圓盤需要移動,則有如下處理:
1.如果希望從“左”移到“中”,打印“Move 1 from left to mid”
2.如果希望從“中”移到“左”,打印“Move 1 from mid to left”
3.如果希望從“中”移到“右”,打印“Move 1 from mid to right”
4.如果希望從“右”移到“中”,打印“Move 1 from right to mid”
5.如果希望從“左”移到“右”,打印“Move 1 from left to mid"和“Move 1 from mid to right"
6.如果希望從“右”移到“左”,打印“Move 1 from right to mid”和“Move 1 from mid to left”
以上過程就是遞歸的終止條件,也就是隻剩上層圓盤時的打印過程。

遞歸體:
接下來,我們分析剩下多個圓盤的情況。
如果剩下N個圓盤,從最上到最下依次爲1~N,則有如下判斷:

1.如果剩下的N個圓盤都在“左”, 希望全部移到“中”, 則有三個步驟。
1)將1~N-1個圓盤先全部從“左”移到“右”,明顯交給遞歸過程。
2)將第N個圓盤從“左”移到“中”。
3)再將1~N-1個圓盤全部從“右”移到“中”,明顯交給遞歸過程。

2.如果把剩下的N個圓盤從“中”移到“左”,從“中”移到“右”,從“右”移到“中”,
過程與情況1同理,一樣是分解爲三步,不再詳述。

3.如果剩下的N個圓盤都在“左”,希望全部移到“右”,則有五個步驟。
1)將1~N-1個圓盤先全部從“左”移到“右”,明顯交給遞歸過程。
2)將第N個圓盤從“左”移到“中”。
3)將1~N-1個圓盤全部從“右”移到“左”,明顯交給遞歸過程。
4)將第N個圓盤從“中”移到“右”。
5)最後將1~N-1個圓盤全部從“左”移到“右”,明顯交給遞歸過程。

4.如果剩下的N個圓盤都在“右”, 希望全部移到“左”, 過程與情況3同理,一樣是.分解爲五步,在此不再詳述。

總結:假設有N個圓盤,什麼時候結束遞歸?當只剩一個圓盤時(最上面那一個),根據它的起始杆和目標杆去移動,編碼時總結移動規律,使代碼更簡便。遞歸方法分幾類?兩類,第一類,只要起始杆或者目標杆有一個是mid(中間杆),那麼移動分爲三步;第二類,其餘情況移動分爲五步。

代碼展示

import java.util.Scanner;
public class HanNuoTa {
  public static void main(String[] args) {
 Scanner cs = new Scanner(System.in);
 System.out.print("盤子個數: ");
 int num =cs.nextInt();
 System.out.print("起始位置: ");
 String from = cs.next();
 System.out.print("終止位置: ");
 String to = cs.next();
 int n = Pro(num,"left","mid","right",from,to);
 System.out.println("It will move "+n+" steps.");

}
 static int Pro(int num, String left, String mid, String right, String from, String to) {
 if(num<1) return 0;
 else return process(num,left,mid,right,from,to);
 }
 static int process(int num, String left, String mid, String right, String from, String to) {
  if(num==1) { //遞歸頭
   if(from.equals(mid)||to.equals(mid)) {
    System.out.println("move "+ num +" from "+ from +" to "+ to);
    return 1;
   }
   else {
    System.out.println("move "+ num +" from "+ from +" to "+mid);
    System.out.println("move "+ num +" from "+ mid +" to "+to);
    return 2;
   }
  }
  //遞歸體
  if(from.equals(mid)||to.equals(mid)) {
   String another = (from.equals(left)||to.equals(left))?right:left;
   int part1 = process(num-1,left,mid,right,from,another);
   int part2 = 1;
   System.out.println("move "+ num +" from "+ from +" to "+ mid);
   int part3 = process(num-1,left,mid,right,another,to);
   return part1+part2+part3; //共三步
  }
  else {
   int part1 = process(num-1,left,mid,right,from,to);
   int part2 = 1;
   System.out.println("move "+ num +" from "+ from +" to "+ mid);
   int part3 = process(num-1,left,mid,right,to,from);
   int part4 = 1;
   System.out.println("move "+ num +" from "+ mid +" to "+to);
   int part5 = process(num-1,left,mid,right,from,to);
   return part1+part2+part3+part4+part5; //共五步
  }
 }
}

對next和nextLine存在問題的點擊這裏

結果展示
在這裏插入圖片描述
個人經驗總結,如有問題,歡迎指正討論
( 思想源自左程雲老師的程序員代碼面試指南 )

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