馬周遊問題非遞迴演算法(不要求回到起點)

2021-07-02 21:58:46 字數 3941 閱讀 3994

一、

原題中文大意

;對於乙個8*8的棋

盤,用下列的方式編號

如果它走63步正好經過除起點外的其他位置各一次,這樣一種走法則稱馬的周遊路線,設計乙個演算法,從給定的起點出發,找出它的一條周遊路線。馬的走法是「日」字形路線。

input

輸入有若干行。每行乙個整數n(1<=n<=64),表示馬的起點。最後一行用-1表示結束,不用處理。

output

對輸入的每乙個起點,求一條周遊線路。對應地輸出一行,有64個整數,從起點開始按順序給出馬每次經過的棋盤方格的編號。相鄰的數字用乙個空格分開。 二、

演算法思想及解題用到的主要資料結構

;最本質的還是圖的遍歷的問題。這裡使用深度優先搜尋,不用廣度優先是因為通常在尋找路徑或者迷宮之類的題目中,都是探尋的問題,用深度優先比廣度優先容易比較快地找到解。

涉及到回溯演算法,即走到乙個「死胡同」後,返回上一步,選擇其他方向。這裡就是到達乙個不能往其他任何方向走的位置。

回溯和深度優先確定了,然後就是選擇非遞迴演算法,因為自己確實理解能力有限,遞迴的方法不能完全想明白。於是就自己思考加上查閱資料,使用非遞迴演算法。

其中要提高效率,需要剪枝,即每次選擇下一位置時,要通過一定篩選策略選擇比較快速高效的一步。這裡選擇下一步位置具有最少可行步數的一步。

主要資料結構: 1

、路徑的樹節點,使用自定義結構體,

struct

node;

2、每乙個位置對應的走「日」字的方向陣列

intdirx[8] = ;  

// x

方向陣列

intdiry[8] = ; 

// y

方向陣列

queuestates; 3

、記錄訪問順序的陣列

intseq[64];  

// 記錄走的過程 4

、深度優先回溯中用到的棧

stack

<

node

> t_route;  

// 樹節點棧 5

、標記陣列

intboard[8][8]; 

// 記錄已訪問的位置,已訪問為

1 ,未訪問為0

三、詳細解題思路

1、 對於每組資料,首先把board訪問陣列清零,step值置0,宣告乙個node棧的t_route。

將起始位置初始化為乙個node結點,壓棧。 2

、進入乙個while迴圈,迴圈判斷條件是棧不為空。

取棧頂元素,seq陣列中step下標位置為改棧頂元素的num值,記錄路徑, step++。如果此時step的值等於64,則說明走完了整個棋盤,退出迴圈。

同時,board陣列也將棧頂元素對應座標位設為1(已訪問) 3

、針對棧頂元素,遍歷它的8個鄰居節點,在可行的鄰居節點中選擇可行步數最少的乙個。

找到後,用flag記錄該鄰居節點的下標值,未找到則flag為-1 4

、找到乙個可行節點,將棧頂元素的neighbor陣列對應下標位設為1(已訪問)將該節點的值初始化(x,y,num,neighbor[8]),壓棧。

棧頂元素沒有乙個可行節點,將step減1,board陣列對應下標位置設為0,出棧,回溯。 5

、判斷step是否等於64,若是,則找到解,一次輸出seq陣列的值;反之,沒有解,輸出-1。

其中計算乙個位置是否可以到達,呼叫函式canmove判斷,判斷條件為該位置座標不越界合法並且board陣列中對應下標位置為0。

每個node裡面有二維陣列的座標值和本身陣列,用乙個轉換函式xy_to_num,可以利用座標值算出數值。

計算下一步的可行步數時,呼叫函式

next_neighbor

計算,函式中用canmove函式計算。 四、

逐步求精演算法描述(含過程及變數說明)

變數及函式說明

// 非遞迴dfs

中樹節點的結構體

struct

node

;                                      

intdirx[8] = ;  

// x

方向陣列

intdiry[8] = ; 

// y

方向陣列

intseq[64];  

// 記錄走的過程

intstep;    

// 指向

seq對應的下標進行賦值

, 每走一步

step + 1

,回溯一次

step - 1

intboard[8][8]; 

// 記錄已訪問的位置,已訪問為

1 ,未訪問為0

intxy_to_num(

intx, 

inty);     

// 計算座標對應數值,引數為座標

bool

canmove(

intx, 

inty);      

// 計算該位置是否可行,引數為座標

intnext_neighbor(

intx, 

inty); 

// 計算下一位置的可行步數

,引數為座標

初始化起始位置結點,

board

陣列,step;

標記board

陣列,初始結點壓棧;

while

(棧不為空)

else }

if (step == 64)  // 

找到解

輸出seq陣列

else

輸出「-1「

五、程式注釋清單(重要過程的說明)

;

#include#include#include#include#include#includeusing namespace std;

// 非遞迴dfs中樹節點的結構體

struct node ;

int dirx[8] = ; // x方向陣列

int diry[8] = ; // y方向陣列

int seq[64]; // 記錄走的過程

int step; // 指向seq對應的下標進行賦值, 每走一步step + 1,回溯一次step - 1

int board[8][8]; // 記錄已訪問的位置,已訪問為 1 ,未訪問為0

int xy_to_num(int x, int y); // 計算座標對應數值,引數為座標

bool canmove(int x, int y); // 計算該位置是否可行,引數為座標

int next_neighbor(int x, int y); // 計算下一位置的可行步數,引數為座標

int main()

t_route.push(ini);

while (!t_route.empty()) }}

// 找到最小步數的鄰居節點

if (flag != -1)

t_route.push(newnode);

}// 棧頂節點沒有可以行走的下一位置,出棧,設為未訪問

else

}

// output

if (step == 64)

else

printf("-1\n");

} // end of if (判斷起始位置是否合法)

else

printf("-1\n");

}return 0;}

int xy_to_num(int x, int y)

bool canmove(int x, int y)

int next_neighbor(int x, int y)

馬踏棋盤的貪心演算法 遞迴演算法 非遞迴演算法實現

include int board 8 8 int htry1 8 int htry2 8 bool chech 8 8 標記位置是否已經被占用 int n 1 就算已走的步數 void findway int i,int j n board i j 0 chech i j false next r...

二叉樹後根周遊的非遞迴演算法

二叉樹後根周遊的非遞迴演算法 include stdafx.h include include typedef char datatype struct bintreenode 二叉樹中結點 typedef struct bintreenode pbintreenode 結點的指標型別 int in...

八皇后問題遞迴和非遞迴演算法

大名鼎鼎的八皇后問題。相信大家都耳熟能詳。八皇后的是乙個典型的用回溯法求解的問題。在回溯法中的乙個關鍵是要動態儲存求解空間對應的程式所處的狀態,特別是能夠進行狀態 回滾 當一發現個部分解再往下去不能成為合法的解時,要回溯到這個部分解之前所處的狀態。程式狀態的 前進 和 回滾 用ban和unban函式...