题目描述:
- 使用深度优先的方法结局迷宫问题
- 规定迷宫的最外围为一圈墙,给出起点和终点要求输出一条正确的路径,但不要求为最短路径
- 使用
opencv
实现可视化,演示一个动态求解迷宫的过程
算法描述:
- 深度优先:
DFS
,在该问题中表现为如果在当前位置有可以通行的方向,则探索该方向,直到找到终点或者当前位置无可通行方向时,回溯到上一个位置。重复以上步骤,直到到达终点或者无法求解为止 - 对于以上探索和回溯的过程,一般有 递归 和 栈 两种方式模拟这个过程。效率相比栈的效率更高,但是递归实现的代码更加简单。在这里采用 栈 的数据结构模拟这个过程。
- 迷宫的表示:采用二维数组保存地图信息,
0
表示墙,1
表示路 opencv
可视化:定义颜色way_color
,wall_color
,passed_color
,首先初始化绘制迷宫,根据墙或者路分别绘制成不同颜色,结合栈易于实现,每次将入栈的位置区域颜色绘制成passed_color
,在此时使用waitkey(500)
函数等待500ms,对于出栈的元素绘制成way_color
,同样waitkey(500)
,经过上述操作可将迷宫求解动态化
结果展示:
- 迷宫可视化:
- 结果
问题分析:
- 理解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;
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;
}
#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);
}
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)
{
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
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