貪婪+回溯演算法------迷宮問題(遞迴實現)
阿新 • • 發佈:2018-12-10
前提
- 很明顯,初始迷宮的路和牆需要定義和儲存,(這裡用的迷宮用陣列儲存,用1表示牆,用0表示未走過的路。)
- 需要明確判斷下一步朝哪個方向走?(這裡的方向是:下->右->上->左,這裡將方向用一個二維陣列來儲存)
- 如何判斷下一步是否在迷宮外?這裡的處理是在迷宮陣列外加“一堵牆”。這堵牆設值為1。
演算法過程描述
- 思路:從當前位置獲取下一步路徑的位置,判斷下一步路徑的位置是否走過,是不是牆。若始終未找到下一步路徑的位置,則返回上一次判斷過程,重新選擇一個新的路徑。如此反覆,直至達到出口的位置,輸出完整的迷宮路徑。
- 問題:如何判斷下一步位置是否已經走過或者不是牆?
- 解決方案:
- 第一種解決方案:使用棧來儲存迷宮路徑位置。當執行判斷語句時,掃描全棧,判斷這個位置是否已經在棧中。(時間複雜度增加)
- 第二種解決方案:再次定義一個與迷宮(不包含圍牆)大小一樣的二維陣列temp。如果這個位置能夠成為迷宮路徑,則將temp陣列中與這個位置擁有相同座標的陣列元素設為2。(空間複雜度增加)
- 第三種解決方案:在原有的迷宮陣列中,將能夠成為路徑的位置的陣列元素設為2(時間複雜度和空間複雜度最優)
- 儲存結構 儲存下一個路徑的方向增量陣列
//定義一個數組儲存一個格子可以移動的方向增量
//二維陣列的行座標表示x軸的座標,列座標表示y軸的座標
int move[4][4] = {
{1,0},//下面一格
{0,1},//右面一格
{-1,0},//上面一格
{ 0,-1}//左面一格
};
迷宮初始化定義
#define r 7//迷宮的行數
#define c 8//迷宮的列數
//定義迷宮陣列,用0表示這個格子的路,用1表示這個格子是牆,用2表示這個格子走過。
//迷宮外圍必須有一堵牆
int M[r+2][c+2] = {
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,1,1,1,0,0,1},
{1,0,1,0,0,1,0,0,1,1},
{1,0,1,0,0,0,0,1,1,1},
{1,0,0,1,0,1,0,1,1,1},
{1,1,0,1,1,1,0,1,1,1},
{ 1,0,0,1,0,0,0,1,1,1},
{1,1,0,0,0,1,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1},
};
一組解與多組解的不同實現
- 一組解:遞迴方法是有返回值的,用以區分是找到一條完整路徑返回上一步操作還是沒有找到下一步路徑返回上一步操作。若是找到一條路徑返回的,則只需要輸出當前位置即可,無須,將當前的位置設為0(未走過的路)。
- 多組解:遞迴函式是無返回值的。因為無論是上述的哪一種情況,都需要將當前的位置設為0(未走過的路)。
- 程式碼:
//遞迴實現(求的是一組解)
int Maze_1(int x,int y)
{
if (x == 7 && y == 8)
{
return 1;
}
//找到下一個格子可以進入
for (int i = 0; i < 4; ++i) {
//獲取下一格位置
int a = move[i][0] + x;//獲取橫座標
int b = move[i][1] + y;//獲取縱座標
//表示路未走過
if (M[a][b] == 0)
{
M[a][b] = 2;//表示路已經走過
//當遞迴返回的值為1時,表示找到路徑
//當遞迴返回值為0時,表示當前找到的位置不能成為路徑,遞迴返回當上一次,重新尋找位置
tag = Maze_1(a,b);
if (tag)
{
printf("(%d,%d)->",a,b);
return tag;
}
}
}
}
//列印路徑
void print()
{
for (int i = 1; i < 8; ++i) {
for (int j = 1; j < 9; ++j) {
if(M[i][j]==2)
{
printf("(%d,%d)->",i,j);
}
}
}
}
void Maze_2(int x ,int y)
{
M[x][y] = 2;//表示當前位置已經走過
if (x == 7 && y == 8)
{
print();
printf("\n");
} else{
//找到下一個格子可以進入
for (int i = 0; i < 4; ++i) {
//獲取下一格位置
int a = move[i][0] + x;//獲取橫座標
int b = move[i][1] + y;//獲取縱座標
//表示路未走過
if (M[a][b] == 0)
{
Maze_2(a,b);
M[a][b] = 0;
}
}
}
}