在n x n棋盤(有n x n個格點的棋盤)的某個格點上有一箇中國象棋馬,馬走日字。java
求一條周遊棋盤的路徑,使得馬可以從起始位置起沿着該路徑每一個格點剛好走一次最後回到出發位置。node
一、初期思路:算法
首先想到的是用DFS來解決,不只能夠遍歷全局還能夠回溯,因而着手作了起來,雖然是DFS,可是在此題中,不須要用到鄰接矩陣,也不須要數組來判斷每點是否到過,一開始的設想是利用二維數組當成棋盤,默認爲全0,初始點是1,第二步就是2,這樣一直走下去,直到走滿,每次須要判斷八個方向,若是該往該方向走後仍在棋盤中,就接着判斷下一步的方向,直到八個方向都走過了或棋盤已經走滿,就返回上一步棋。數組
二、回溯的方法:ide
程序使用遞歸思想,每一步都會建立一個優先隊列,在未完成算法的前提下,只要優先隊列不爲空,就會一直執行下去。函數
三、遇到問題:this
但在實際解決過程當中,每次遞歸須要傳入當前棋盤數組,但返回以前步數時數組總是會被更改,因此就嘗試了用字典來保存每步棋,奇怪的是每次操做都會改變全部字典中的值,使得實驗出錯,改用三維數組後也遇到了一樣的問題。spa
淺拷貝和深拷貝問題:.net
這兩種複製和clone函數都出現了相同的問題,在百度後瞭解到這涉及了淺拷貝和深拷貝,淺拷貝過程當中只是引用了數組的地址(實際指向同一個空間),深拷貝纔是真正開闢了新的空間code
解決方法:爲了不麻煩就直接本身用兩個for循環來複制數組,運行,獲得正常的結果。
還有一個是用java自帶函數時,上圖最後一條複製語句會對全部二維數組賦值而不是當前二維數組,在室友電腦上試事後也是同樣。(未解決)
有了以前的思路,雖然可以解決問題,但仍是遠遠不夠的,在棋盤爲8*8時幾乎跑不出答案,咱們須要思考剪枝方案使代碼更快運行。
因爲涉及到特定問題的剪枝須要對問題作細緻研究,因此就直接百度了剪枝的方案以下:
根據剪枝方案來改造本身的代碼:
一、 爲了計算每一個位置的可走方向數,咱們須要添加一個方法來判斷某個位置有幾種走法,只須要對當前點進行八個方向的遍歷便可,難點是優先選擇可走步數少的點,一開始我每次都走最優勢,但發現這樣沒法走到終點,且沒法再回溯,因此咱們是須要將每一步的全部可走點的可走步數保存起來的,這樣在走錯時纔可能進行回溯,這裏用到了優先隊列這個結構。
使用優先隊列免去了本身去寫建立數組和須要的排序算法,咱們只須要給優先隊列制定排列規則便可,而無需搞懂其內部的具體實現。這帶來了很大的便利。
二、 添加一個方法來計算某個位置到中心的距離,而後在優先隊列中加入距離的比較便可實現剪枝2。
三、 最終實現的動態圖
以DFS爲主要算法,時間複雜度(V次遍歷+ E次遞歸)
假設每一個頂點都有八種走法,遞歸次數最可能是8^N(N*N棋盤中)
有時每種狀況下的時間相差很大,存在必定的特殊性(剪枝不必定是完美的)
1 public class Horse { 2 static int n;// n*n棋盤 3 static int FP[][] = { { 1, 2 }, { 1, -2 }, { -1, 2 }, { -1, -2 }, { 2, 1 }, { 2, -1 }, { -2, 1 }, { -2, -1 } };// 可能走的八個方向 4 static int x0, y0;// 起始點 5 static int[][][] group; 6 7 class node {// 當前點,座標及可走方向數量 8 int x; 9 int y; 10 int hp;//可走方向數 11 int dc;//距離中心距離 12 13 public node(int i, int j, int k,int d) { 14 this.x = i; 15 this.y = j; 16 this.hp = k; 17 this.dc=d; 18 } 19 } 20 21 public static Comparator<node> idComparator = new Comparator<node>() {//優先隊列的比較方法(小到大 遠到近 22 @Override 23 public int compare(node n1, node n2) { 24 if(n1.hp != n2.hp) { 25 return (int) (n1.hp - n2.hp); 26 }else { 27 return n2.dc-n1.dc; 28 } 29 } 30 }; 31 32 public void init() { 33 Scanner sc = new Scanner(System.in); 34 System.out.println("int n:"); 35 n = sc.nextInt(); 36 group = new int[n * n + 1][n][n]; 37 System.out.println("請輸入起始點:"); 38 x0 = sc.nextInt(); 39 y0 = sc.nextInt(); 40 DFS(x0, y0, 1); 41 } 42 43 public void DFS(int x, int y, int now_pace) {// 進行馬的深度遍歷 44 if (check(x, y, now_pace)) { 45 return; 46 } 47 copy(group[now_pace], group[now_pace - 1]); 48 group[now_pace][x][y] = now_pace + 1; 49 now_pace++; 50 System.out.println(now_pace); 51 ps(group[now_pace - 1]); 52 System.out.println(); 53 54 if ((now_pace == n * n + 1 && ((Math.pow(x - x0, 2) + Math.pow(y - y0, 2) == 5)))) {// 判斷是否走滿且能回到原點 55 System.out.println("okkkkk"); 56 System.exit(0); 57 return; 58 } 59 60 Queue<node> nodePriorityQueue = new PriorityQueue<>(8, idComparator);// 每次來個優先隊列從小到大 61 for (int[] p : FP) {//遍歷八個方向放入優先隊列 62 //int nphs = nextPosHasSteps(x + p[0], y + p[1], now_pace); 63 nodePriorityQueue.add(new node(x + p[0], y + p[1], nextPosHasSteps(x + p[0], y + p[1], now_pace),disFromCenter(x, y))); 64 } 65 while (!nodePriorityQueue.isEmpty()) {//回溯 66 node n = nodePriorityQueue.poll(); 67 DFS(n.x, n.y, now_pace); 68 } 69 } 70 71 public boolean check(int x, int y, int now_pace) {// 判斷是否在棋盤內或已經到過 72 return x < 0 || x >= n || y < 0 || y >= n || group[now_pace - 1][x][y] != 0; 73 } 74 75 public int nextPosHasSteps(int x, int y, int now_pace) {// 計算當前位置可走的方向 76 int steps = 0; 77 for (int[] p : FP) {// 遍歷八個方向進行判斷 78 if (!check(x + p[0], y + p[1], now_pace)) { 79 steps++; 80 } 81 } 82 return steps; 83 } 84 85 public int disFromCenter(int x,int y) {//距離中心距離 86 return (int) (Math.pow(x-n/2, 2)+Math.pow(y-n/2, 2)); 87 } 88 89 public void ps(int[][] s) {//打印數組 90 for (int i = 0; i < s.length; i++) { 91 for (int j = 0; j < s.length; j++) { 92 System.out.print(s[i][j] + " "); 93 } 94 System.out.println(); 95 } 96 } 97 98 public void copy(int[][] a, int[][] b) { 99 for (int i = 0; i < a.length; i++) { 100 for (int j = 0; j < a.length; j++) { 101 a[i][j] = b[i][j]; 102 } 103 } 104 } 105 106 public static void main(String[] args) { 107 Horse h = new Horse(); 108 h.init(); 109 } 110 }