馬踏棋盤(騎士周遊問題),深度優先搜尋
阿新 • • 發佈:2018-12-18
馬踏棋盤問題(又稱騎士周遊或騎士漫遊問題是演算法設計的經典問題之一)。問題描述:國際象棋的棋盤是8×8的方格棋盤,先將馬放在任意指定的方格中,按照馬走棋的規則將馬進行移動,要求每個方格只能進入一次,最終使得馬走遍棋盤的64個方格。除了邊緣的位置,馬每次有8種走法。 雖然遞迴,回溯法效率很低,但是也是一種解決方式。就是按照一條路走到底碰壁了在回來走另一條路。就如上圖所示,馬開始走,有8種選擇,比如現在選擇第一種方式,然後在1的位置又有8種選擇,照著下圖這樣走,如果有哪一次走不通了,就往回退,直到退到初始位置,說明從該位置不能遍歷整個棋盤 程式碼如下
#include <stdio.h> #include <time.h> //包含該標頭檔案,以便計算程式用時 //棋盤大小 #define X 8 #define Y 8 //用二維陣列表示棋盤 int chess[X][Y]; //列印棋盤,棋盤是全域性變數,這裡就不寫函式引數了 void print() { for (int i = 0; i < Y; i++) { for (int j = 0; j < X; j++) { //輸出長度為2,不足左補空格 printf("%2d ", chess[i][j]); } printf("\n"); } printf("\n"); } //x,y表示當前位置,count表示相對於位置(x,y)的8種走法中該走哪一種 int nextxy(int &x, int &y, int count) { //8種選擇是x加兩格,x退兩格與y退一格,y加一格組合,有四種 //x退一格,x加一格與y加兩格,y退兩格組合,有四種,一共8種 switch (count) { case 0: //首先位置要有效,其次該位置沒有走過 if (x + 2 <= X - 1 && y + 1 <= Y - 1 && chess[x + 2][y + 1] == 0) { //如果有效位置,則將(x,y)改為馬下一次要到的位置 x += 2; y += 1; return 1; } break; case 1: //首先位置要有效,其次該位置沒有走過 if (x + 2 <= X - 1 && y -1 >= 0 && chess[x + 2][y - 1] == 0) { //如果有效位置,則將(x,y)改為馬下一次要到的位置 x += 2; y -= 1; return 1; } break; case 2: //首先位置要有效,其次該位置沒有走過 if (x - 2 >= 0 && y - 1 >= 0 && chess[x - 2][y - 1] == 0) { //如果有效位置,則將(x,y)改為馬下一次要到的位置 x -= 2; y -= 1; return 1; } break; case 3: //首先位置要有效,其次該位置沒有走過 if (x - 2 >= 0 && y + 1 <= Y-1 && chess[x - 2][y + 1] == 0) { //如果有效位置,則將(x,y)改為馬下一次要到的位置 x -= 2; y += 1; return 1; } break; case 4: if (x + 1 <= X - 1 && y + 2 <= Y - 1 && chess[x + 1][y + 2] == 0) { x += 1; y += 2; return 1; } break; case 5: if (x + 1 <= X - 1 && y - 2 >= 0 && chess[x + 1][y - 2] == 0) { x += 1; y -= 2; return 1; } break; case 6: if (x - 1 >= 0 && y + 2 <= Y - 1 && chess[x - 1][y + 2] == 0) { x -= 1; y += 2; return 1; } break; case 7: if (x - 1 >= 0 && y - 2 >= 0 && chess[x - 1][y - 2] == 0) { x -= 1; y -= 2; return 1; } break; default: break; } //返回0,需要重新尋找 return 0; } //遍歷演算法 //x,y表示位置,tag表示當前走了幾格,第一次開始時該值應為1 int TravelChess(int x, int y, int tag) { chess[x][y] = tag; //如果走了X*Y格,說明全部遍歷完,列印棋盤 if (tag == X*Y) { print(); return 1;//返回1表示遍歷完成 } //如果沒有遍歷完 int x1 = x, y1 = y; //尋找下一個有效位置 int count = 0; int flag = nextxy(x1, y1, count); //不斷自增count,直到找到下一個有效位置 while (flag == 0 && count <= 7) { count++; flag = nextxy(x1, y1, count); } //如果找到了下一個有效位置,該有效位置作為(x,y),繼續呼叫自身進行相同的搜尋 while (flag) { //如果遍歷全部完成逐層返回1; if (TravelChess(x1, y1, tag + 1)) { return 1; } //否則說明從該次的(x,y)往後走沒有走通,重新尋找該次呼叫的下一個有效位置 x1 = x, y1 = y; count++; flag = nextxy(x1, y1, count); while (flag == 0 && count <= 7) { count++; flag = nextxy(x1, y1, count); } } //如果沒有找到有效位置,則將棋盤該位置重新賦值為0 if (0 == flag) { chess[x][y] = 0; } //返回0代表這種走法不通,讓上一層呼叫重新尋找有效位置 return 0; } int main() { //記錄開始時間 clock_t start = clock(); //以防萬一,初始化棋盤都為0 for (int i = 0; i < Y; i++) { for (int j = 0; j < X; j++) { chess[i][j] = 0; } } printf("請輸入馬的初始位置:"); int x, y; scanf("%d,%d", &x, &y); if (!TravelChess(x, y, 1)) printf("馬踏棋盤失敗了,請更換起始位置!\n"); clock_t end = clock(); printf("本次執行一共用時:%f", (double)(end - start) / CLOCKS_PER_SEC); return 0; }
用深度優先搜尋寫的這個實在是太慢了,跑了很久不出結果,我也很絕望,於是就將棋盤換成了55的試了一下很快就出結果了!88的跑不出來,不知道自己的程式碼有沒有問題,用5*5的試了一下,感覺沒啥問題。