经典迷宫问题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);
}





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