算法 - 迷宫问题求解(c++,DFS,opencv可视化)


题目描述:
  • 使用深度优先的方法结局迷宫问题
  • 规定迷宫的最外围为一圈墙,给出起点和终点要求输出一条正确的路径,但不要求为最短路径
  • 使用opencv实现可视化,演示一个动态求解迷宫的过程
算法描述:
  • 深度优先:DFS,在该问题中表现为如果在当前位置有可以通行的方向,则探索该方向,直到找到终点或者当前位置无可通行方向时,回溯到上一个位置。重复以上步骤,直到到达终点或者无法求解为止
  • 对于以上探索和回溯的过程,一般有 递归 两种方式模拟这个过程。效率相比栈的效率更高,但是递归实现的代码更加简单。在这里采用 的数据结构模拟这个过程。
  • 迷宫的表示:采用二维数组保存地图信息,0表示墙,1表示路
  • opencv可视化:定义颜色way_color,wall_color,passed_color,首先初始化绘制迷宫,根据墙或者路分别绘制成不同颜色,结合栈易于实现,每次将入栈的位置区域颜色绘制成passed_color,在此时使用waitkey(500)函数等待500ms,对于出栈的元素绘制成way_color,同样waitkey(500),经过上述操作可将迷宫求解动态化
结果展示:
  • 迷宫可视化:
    迷宫可视化
  • 结果
    way
问题分析:
  • 理解DFS,利用栈的特性实现完美回溯
  • opencv的配置问题,使用cmake编译自己对应平台的可执行文件,具体教程可自行百度
  • 路径何时入栈和出栈需要明白,只有到当前位置确定可以通行是才入栈,只有当前位置的所有方向均探索完毕且都无法通行时才出栈
代码展示:
  • 代码包含三个部分,maze.h,main.cpp,maze.dat
  • main.cpp:
    #include <iostream>
    #include "maze.h"

    using std::cin;
    using std::cout;
    using std::endl;

    int main()
    {
        if(freopen("../maze.dat", "r", stdin) == NULL)
        {
            std::cout << "freopen failed" << std::endl;
            exit(0);
        }
        int m,n;    //m,n 表示迷宫的规模,m行n列
        cin >> m >> n;

        Maze maze(m,n);
        cin >> maze.start.row >> maze.start.col;
        cin >> maze.end.row >> maze.end.col;
        for (int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                cin >> maze.map[i][j].isWay;
                maze.map[i][j].place.row = i;
                maze.map[i][j].place.col = j;
            }
        }

        Mat img = Mat::zeros(40 * maze.row, 40 * maze.col, CV_8UC3 );
        drawMaze(img,maze);

        stack<Grid*> t_solution = maze_solution(maze,img);
        stack<Grid*> solution;
        while (!t_solution.empty())
        {
            solution.push(t_solution.top());
            t_solution.pop();
        }
        if(solution.empty())
        {
            cout << "No solution";
        } else
        {
            while (!solution.empty())
            {
                cout << solution.top()->place;
                solution.pop();
                if(!solution.empty())
                    cout<< "->";
            }
            cout << endl;
        }

        return 0;
    }
  • maze.h:
    #ifndef MAZE_MAZE_H
    #define MAZE_MAZE_H

    #include <stack>
    #include "opencv2/imgproc.hpp"
    #include "opencv2/highgui.hpp"
    #include <iostream>

    using namespace std;
    using namespace cv;

    struct M_Point
    {
        int row;
        int col;

        bool operator== ( const M_Point &p)
        {
            return ((this->row == p.row) && (this->col == p.col));
        }
        friend ostream& operator<< (ostream &os, M_Point &point)
        {
            os << '(' << point.row << ',' << point.col << ')';
            return os;
        }
    };

    struct Grid
    {
        M_Point place;
        int direction = 0;
        bool isWay;
        bool isPassed = false;
    };

    struct Maze
    {
        int row;
        int col;
        M_Point start;
        M_Point end;
        Grid** map = NULL;

        Maze(int m, int n) :row(m), col(n)
        {
            if (m == 0 || n == 0)
                return;
            map = new Grid*[m];
            for (int i = 0; i < m; ++i)
            {
                map[i] = new Grid[n];
            }
        }

        ~Maze()
        {
            if (map == NULL || row == 0 || col == 0)
                return;
            else
            {
                for (int i = 0; i < row; ++i)
                {
                    delete[](map[i]);
                }
                delete[](map);
            }
        }
    };

    const Scalar wall_color(200, 200, 200);
    const Scalar way_color(50, 70, 20);
    const Scalar pass_color(100, 150, 100);
    const Scalar start_end_color(150,100,100);
    const int gap = 2;
    const int delay = 200;

    void drawBlock(Mat &img, int row, int col, int height, int width, Scalar color)
    {
        Point p1 = Point(col * width + gap, row * height + gap);
        Point p2 = Point((col + 1) * width - gap, (row + 1) * height - gap);
        rectangle(img, p1, p2, color, -1);
    }


    void drawMaze(Mat &img, const Maze &maze)
    {
        int height = img.rows / maze.row;
        int width = img.cols / maze.col;

        for (int i = 0; i < maze.row; ++i)
        {
            for (int j = 0; j < maze.col; ++j)
            {
                if (maze.map[i][j].place == maze.start || maze.map[i][j].place == maze.end)
                {
                    drawBlock(img, i, j, height, width, start_end_color);
                }
                else
                {
                    if (maze.map[i][j].isWay)
                        drawBlock(img, i, j, height, width, way_color);
                    else
                        drawBlock(img, i, j, height, width, wall_color);
                }
            }
        }
        imshow("maze", img);
        waitKey(0);
    }

    //m表示行,n表示列
    stack<Grid*> maze_solution(const Maze &maze, Mat &img)
    {
        int height = img.rows / maze.row;
        int width = img.cols / maze.col;

        Grid** map = maze.map;
        stack<Grid*> way;

        //起点或终点不在迷宫内
        if (maze.start.row >= maze.row || maze.start.col >= maze.col
            || maze.end.row >= maze.row || maze.end.col >= maze.col)
            return way;

        Grid *current = &map[maze.start.row][maze.start.col];
        do
        {
            //当前的位置是可行路径且之前未经过
            if (current->isWay && !current->isPassed)
            {
                current->isPassed = true;
                way.push(current);

                //如果到达终点,则返回该条路径
                if (current->place == maze.end)
                {
                    waitKey(0);
                    return way;
                }
                else
                {
                    if(!(current->place == maze.start))
                        drawBlock(img, current->place.row, current->place.col, height, width, pass_color);
                    imshow("maze", img);
                    waitKey(delay);
                    //默认每次先探索右边的方向
                    current = &map[current->place.row][current->place.col + 1];
                }
            }
            else
            {
                //当前路径非空,则继续探索当前格的其余方向直到所有方向都探索完成
                while (true)
                {
                    if (!way.empty())
                    {
                        current = way.top();
                        ++current->direction;

                        //探索四个方向
                        if (current->direction != 4)
                        {
                            //direction可能值有1,2,3分别代表下,左,上
                            switch (current->direction)
                            {
                                case 1://探索下边的格子
                                {
                                    current = &map[current->place.row + 1][current->place.col];
                                    break;
                                }
                                case 2://探索左边的格子
                                {
                                    current = &map[current->place.row][current->place.col - 1];
                                    break;
                                }
                                case 3://探索上边的格子
                                {
                                    current = &map[current->place.row - 1][current->place.col];
                                    break;
                                }
                            }
                            break;
                        }
                        else
                        {    //此路不通
                            way.pop();
                            drawBlock(img, current->place.row, current->place.col, height, width, way_color);
                            imshow("maze", img);
                            waitKey(delay);
                        }
                    }
                    else
                        break;
                }
            }
        } while (!way.empty());
        return way;
    }
    #endif //MAZE_MAZE_H
  • maze.dat
    10 10

    1 1
    8 8
    
    0 0 0 0 0 0 0 0 0 0
    0 1 1 1 1 0 0 0 0 0
    0 1 0 0 1 1 1 0 0 0
    0 1 1 1 0 1 1 0 0 0
    0 0 0 1 0 0 1 1 1 0
    0 0 0 1 1 1 0 0 0 0
    0 0 1 1 0 0 0 0 0 0
    0 0 0 1 1 1 1 1 1 0
    0 0 0 1 0 0 0 0 1 0
    0 0 0 0 0 0 0 0 0 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值