回溯问题

本文介绍了一种基于深度优先搜索的迷宫求解算法,通过使用栈和三元组结构来记录路径,实现了从起点到终点的有效路径寻找。文章详细解释了算法的实现过程,并通过代码示例展示了如何构建迷宫矩阵、标记已访问区域以及实现路径回溯。
摘要由CSDN通过智能技术生成

迷宫问题

题目:给一个二维列表,表示迷宫(0表示通道,1表示围墙)。给出算法,求一条走出迷宫的路径。

                                                                        

解决思路:

  • 在一个迷宫节点(x,y)上,可以进行四个方向的探查:maze[x-1][y], maze[x+1][y], maze[x][y-1], maze[x][y+1]
  • 思路:从一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。
  • 方法:创建一个空栈,首先将入口位置进栈。当栈不空时循环:获取栈顶元素,寻找下一个可走的相邻方块,如果找不到可走的相邻方块,说明当前位置是死胡同,进行回溯(就是讲当前位置出栈,看前面的点是否还有别的出路)

代码

# include <iostream>
# include <vector>
# include <stack>
# include <string>
# include <sstream>
# include <iterator>
using namespace std;

struct PathUnit //三元组结构
{
    int x;  //坐标x
    int y;  //坐标y
    int direct; //方位特征值。0东,1南,2西,3北
};
class MyMaze
{
    vector<vector<int > > vecMaze;  //迷宫描述矩阵
    vector <vector<int> > vecMark;  //迷宫标志矩阵
public:
    MyMaze(int m, int n) :vecMaze(m + 2, vector<int>(n + 2)), vecMark(m + 2, vector<int>(n + 2))
    {
        for (int i = 0; i < m + 2; i++)
        {
            fill(vecMaze[i].begin(),vecMaze[i].end(),1);
        }
    }
    void Initial(string strMaze)//根据字符串建立迷宫矩阵
    {
        istringstream in(strMaze);
        for (int i = 1; i < vecMaze.size() - 1; i++)
        {
            for (int j = 1; j < vecMaze[0].size()-1; j++)
            {
                in >> vecMaze[i][j];
            }
        }
    }
    void showMaze() //显示迷宫矩阵
    {
        for (int i = 0; i < vecMaze.size(); i++)
        {
            copy(vecMaze[i].begin(),vecMaze[i].end(),ostream_iterator<int>(cout,"\t"));
            cout << endl;
        }
    }
    void GetNextPath(const PathUnit& cur, PathUnit& next)   //根据当前位置走向,获得下一结点
    {
        next = cur;
        next.direct = 0;
        switch (cur.direct)
        {
        case 0:
            next.x += 1;
            break;
        case 1:
            next.y += 1;
            break;
        case 2:
            next.x -= 1;
            break;
        case 3:
            next.y -= 1;
            break;
        }
    }
    bool GetOuter(const PathUnit &u)    //当前结点是否是出口结点
    {
        int out_x = vecMaze[0].size() - 2;
        int out_y = vecMaze.size() - 2;
        bool bRet = ((u.x==out_x && u.y == out_y)? true:false);
        return bRet;
    }
    void GetPath()  //迷宫算法函数
    {
        stack <PathUnit> st;
        PathUnit unit;
        unit.x = 1;
        unit.y = 1;
        unit.direct = 0;
        st.push(unit);
        bool bOver = false;
        while (!st.empty())
        {
            PathUnit cur = st.top();    //获得栈顶3元组,并出栈
            st.pop();
            PathUnit next;
            while (cur.direct <= 3) //若有方位可走
            {
                GetNextPath(cur,next);  //获得下一节点三元组
                if (GetOuter(next)) //若是出口节点
                {
                    st.push(cur);   //当前节点入栈
                    st.push(next);  //下一节点入栈
                    bOver = true;   //置结束标志
                    break;  //退出循环
                }
                if (!vecMaze[next.y][next.x] && !vecMark[next.y][next.x])   //合法移动且从未走过
                {
                    vecMark[next.y][next.x] = 1;    //标志矩阵置1,下次不能再走了
                    st.push(cur);   //当前节点入栈
                    cur = next; //当前节点等于下一节点,从下一节点继续跟踪
                }
                else//换当前节点方向,继续跟踪
                {
                    cur.direct++;
                }
            }   //while(cur.direct <= 3)
            if (bOver)
                break;
        }   //while(!st.empty())
        if (bOver)
        {
            while (!st.empty())
            {
                PathUnit& u = st.top();
                cout << "(" << u.x << "," << u.y << "," << u.direct << ")" << endl;
                st.pop();
            }
        }
        else
        {
            cout << "there is no path" << endl;
        }
    }
};
int main()
{
    MyMaze m(6, 6);
    string s = "";
    s = s + "0 0 1 0 1 1 " + "0 1 0 0 1 1 " + "0 0 0 1 1 1 " + "1 0 1 1 1 1 " + "1 0 0 0 0 1 " + "1 1 1 1 0 0 ";  //每个字串后面有空格
    m.Initial(s);
    cout << "显示迷宫矩阵:" << endl;
    m.showMaze();
    cout << "显示3元组表示结点路径" << endl;
    m.GetPath();
    return 0;
}

运行结果 

回溯法与深度优先遍历

1、相同点:

回溯法在实现上也是遵循深度优先的,即一步一步往前探索,而不像广度优先那样,由近及远一片一片地扫。

2、不同点

(1)访问序

深度优先遍历:目的是“遍历”,本质是无序的。也就是说访问次序不重要,重要的是都被访问过了。可以参见题Surrounded Regions,深度优先只需要把从边界起始的'O'全部访问到即可。因此在实现上,只需要对于每个位置记录是否被visited就足够了。

回溯法:目的是“求解过程”,本质是有序的。也就是说必须每一步都是要求的次序。可以参见题Word Search,需要以要求的序进行深度优先探索,必须每一步都符合要求。因此在实现上,不能使用visited记录,因为同样的内容不同的序访问就会造成不同的结果,而不是仅仅“是否被访问过”这么简单。要使用访问状态来记录,也就是对于每个点记录已经访问过的邻居方向,回溯之后从新的未访问过的方向去访问邻居。至于这点点之前有没有被访问过并不重要,重要的是没有以当前的序进行访问。

(2)访问次数

深度优先遍历:已经访问过的节点不再访问,所有点仅访问一次。

回溯法:已经访问过的点可能再次访问,也可能存在没有被访问过的点。

回溯法可以求解所有可能的解。而广度优先利用分支限界的思想,求解最优化问题,广度优先求图中两点最短路径,要求是不带权或者每条边的权值相等。如果带权就只能使用迪杰斯特拉算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值