經典迷宮問題1


1.迷宮問題直觀感受

    下面給出迷宮問題的一個直觀感受圖,下圖中棕色代表通道阻塞,白色代表可用通道,紅色代表起始位置,綠色代表當前位置,黃色代表出口。


迷宮1:



迷宮2




2.迷宮算法


算法基本思想:

  首先將入口設爲當前位置;然後從當前位置出發,按照固定順序(例如:右左上下順序)探測第一個可用下一個單元,然後將這下一個單元設爲當前單元,重複探測,直至遇到出口;如果探測的過程中發現,在當前節點,無論右左上下哪個位置都不能到達下一個可用位置,則退回到當前節點的上一個節點,將上一個節點設爲當前單元,繼續探測。依次類推。


這裏注意幾點:


1)每個走過的單元,必須標記爲已經走過,後面不能再重複走。因爲走過的單元, 分爲兩種,第一種可以找到出口的,那麼你再次走回來,可能陷入同一段路徑的循環,比如 A->B->C->A->B->C。我們要消除這種重複路徑。第二種,是不能找到出口的,既然第一次走過就不能找到出口,爲什麼還要走第二次?所以,走過的單元不能再重複的走。

2)每次從單元格四個方向中選個一個可用單元,要保持對這四個方向的計數,上次嘗試過得方向,下一次就不能重複再試了,反證法可以說明。

3)程序出口問題,要麼是試驗後找到路徑,這時棧裏保存的就是整個路徑;要麼是找不到路徑,那麼棧爲空,因爲你不斷回退,最後到了起點,起點還是沒有路徑,因此退棧後即爲空棧。


#include<iostream>
#include<string>
#include<list>
#define M 8
#define N 8
using namespace std;

/*
每次按照dir的方向進行深度優先搜索,可以走則走並標記,否則,
回溯並清除標記,直到找到右下角的出口。
*/

// 迷宮,Maze[i][j] = 0 代表ij可以走, arr[i][j]=1表示不能走
int Maze[M+2][N+2] = {
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 0, 0, 0, 1, 1, 0, 1, 1, 1},
		{1, 1, 1, 0, 1, 1, 0, 1, 1, 1},
		{1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
		{1, 1, 0, 0, 1, 1, 1, 0, 0, 1},
		{1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 1, 0, 1, 0, 1, 0, 0, 1, 1},
		{1, 0, 0, 1, 1, 1, 0, 0, 0, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};

// 四個方向,分別代表上,下,左,右
int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

// 節點信息,x代表橫座標,y代表縱座標
struct Node
{
	int x;
	int y;
	Node (int x1, int y1):x(x1), y(y1) {}
};


// 打印路徑,使用鏈表,易於輸出
void print_path(list<Node> path)
{
	while(!path.empty())
	{
		cout << "(" << path.front().x << "," << path.front().y << ")" << endl;
		path.pop_front();
	}
}

int DFS(Node cur, Node end, list<Node> &path)
{
    Maze[cur.x][cur.y] = 1;	    // 標記此節點已走過
    path.push_back(cur);		// 將此節點加入路徑棧

	// 當前座標等於結束座標時結束遍歷,並打印路徑
	if(cur.x == end.x && cur.y == end.y)
	{
		print_path(path);	    //打印路徑
		return 1;
	}

	// 從4個方向分別探索
	for(int i = 0; i < 4; ++i)
	{
		// 構造下一個要進行探索的點
		Node next(cur.x + dir[i][0], cur.y + dir[i][1]);
		//	判斷下個點是否可行
		if(Maze[next.x][next.y] == 0)
		{
			// 遞歸進行下一位置的查找
			// 如果一直可以先向下查找,直到找到終點,最底層函數就會返回1,接着返回到倒數第二層,執行if語句,接着返回1。
            // 直到跳出整個遞歸函數。
			if(DFS(next, end, path) == 1)
				return 1;
		}
	}

	// 如果該節點幾個方向均已遍歷,而且都不可行,該節點出棧,回溯
    path.pop_back();

	return 0;
}

int main( )
{
	list<Node> path;			// 存取路徑
	Node sta(1, 1), end(8, 8);	// 記錄開始和結束座標

	if(DFS(sta, end, path) == 0)
		cout << "no path" << endl;
}

 3.迷宮最短路徑算法(使用隊列)

          採用深度優先遍歷時,求出路徑不應定最短;由此,提出以下算法,主要思想是利用隊列,採用廣度優先搜索法,當第一次出現目的點時,中斷搜索,並輸出路徑。

#include <iostream>
#include <vector>
#include <stack>
#define M 8
#define N 8

using namespace std;

// 迷宮,Maze[i][j] = 0 代表ij可以走, arr[i][j]=1表示不能走
int Maze[M+2][N+2] = {
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 0, 0, 0, 1, 1, 0, 1, 1, 1},
		{1, 1, 1, 0, 1, 1, 0, 1, 1, 1},
		{1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
		{1, 1, 0, 0, 1, 1, 1, 0, 0, 1},
		{1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 1, 0, 1, 0, 1, 0, 0, 1, 1},
		{1, 0, 0, 1, 1, 1, 0, 0, 0, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};


// 節點信息,x代表橫座標,y代表縱座標
struct Node
{
	int x;
	int y;
	Node (int x1, int y1):x(x1), y(y1) {}
};

// 虛擬隊列中的節點及其前驅信息
struct QueueElem
{
    int pre;        // 該節點入隊時的前驅節點在虛擬隊列中的位置
    Node node;
    QueueElem(Node node1, int pre1):node(node1), pre(pre1) {}
};

void print_path(vector<QueueElem> &que)
{
    stack<QueueElem> s;

    QueueElem qe = que.back();
    while(qe.pre != -1)
    {
        s.push(qe);
        qe = que[qe.pre];
    }

    while(!s.empty())
    {
        qe = s.top();
        s.pop();
        cout << "(" << qe.node.x << "," << qe.node.y << ")" << endl;
    }
}


void maze_shortest(Node start, Node des)
{
    int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    vector<QueueElem> que;                  // 用於記錄節點入隊順序
    int head = 0;                           // 用於指向隊列頭部

    que.push_back(QueueElem(start, -1));    // 初始點入隊,其前驅爲-1
    while(head < que.size())                // 當虛擬隊列非空時
    {
        Node cur = que[head].node;

        for(int i = 0; i < 4; ++i)
        {
            Node next(cur.x + dir[i][0], cur.y + dir[i][1]);

            if(Maze[next.x][next.y] == 0)   // 下一節點可走
            {
                // 下一節點入隊
                que.push_back(QueueElem(next, head));
                if(next.x == des.x && next.y == des.y)
                {
                    print_path(que);
                    return;
                }
            }
        }
        head++;         // 虛擬出隊
    }
}


int main()
{
    Node start(1, 1), des(8, 8);	// 記錄開始和結束座標

    maze_shortest(start, des);
}





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