1. 题目

数据结构课程,讲完栈,老师留下严蔚敏PPT上的作业,迷宫求解,如下:
请添加图片描述

2. 解题思路

1)采用递归的深度优先搜索,从出发点搜索至结束点
2)递归中自带“栈特性”,在进入当前递归函数相当于栈的push,退出当前递归函数相当于栈的pop,那么根据这个特性,实现搜索地图中返回上一个结点的功能。
具体看代码,代码详细注释,花点时间仔细看看能懂得。如果不懂,网上搜搜深度优先搜索讲解视频。

3. 黑窗口运行效果和代码

请添加图片描述

#include <iostream>
#include <stack>
#include <windows.h>
using namespace std;
// 地图值: 1是墙壁,0是通道,2是主角所在位置,3是出口 4是不能通过的标记
#define WALL 1
#define PASS 0
#define ZHUJUE 2
#define EXITS 3
#define NOPATH 4 

// 全局变量
int m = 10, n = 10;		// 行和列 默认为10
int map[100][100];		// 地图数组,先开大一点,根据m、n限定范围
int vis[100][100];		// 是否访问过,0是没访问,-1是访问过了
bool isFind = false;		// 是否找到出口
stack<int(*)> pathStack;// 用STL模板的栈类记录路径

// 打印地图值
void PrintMap(int flag) {// 根据flag是否清除控制台内容
	if (flag == 0) {
		COORD position;
		position.X = 0;
		position.Y = 0;
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
		cout << "搜索地图中..............." << endl;
	}
	Sleep(250); // 休眠0.25秒,更新输出内容
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++)
		{
			if (map[i][j] == ZHUJUE) {// 主角当前所在位置
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 12);// 红色
				cout << "☆";
				continue;
			}
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);// 白色字体
			if (map[i][j] == WALL || map[i][j] == EXITS) {	// 墙
				cout << "■";
			}
			if (map[i][j] == PASS) {	// 路
				cout << "  ";// 要2个空格
			}
			if (map[i][j] == NOPATH) {	// 不能通过
				cout << "×";
			}
		}
		cout << endl;
	}
	cout << endl;
}
/*
思路:采用深度优先搜索
递归中自带“栈特性”,在进入当前递归函数相当于栈的push,退出当前递归函数相当于栈的pop
那么根据这个特性,实现搜索地图中返回上一个结点的功能。
*/
void DFS(int cury, int curx, int lasty, int lastx, int ysval) {// cury curx 表示当前点, lasty,lastx 表示上一个点,ysval表示上一个点的地图值
	if (isFind) {// 找到了出口,递归栈都返回
		return;
	}
	if (vis[cury][curx] == -1 || map[cury][curx] == 1 || cury >= m || cury < 0 || curx >= n || curx < 0) {// 递归返回条件:这个位置访问过,墙壁,越界,
		return;
	}
	if (map[cury][curx] == EXITS) {// 递归退出条件,找到了出口
		// cout << "找到了出口" << endl;
		isFind = true;
		return;
	}

	// 递归函数执行时,执行相当于栈push时的操作
	pathStack.push(new int[2]{cury, curx});// 记录当前路径
	vis[cury][curx] = -1;				   // 表示访问过当前点
	int curysval = map[cury][curx];			// 保存当前的地图值
	// 更新主角所在位置。
	map[lasty][lastx] = ysval;				// 上一个所在的位置变回地图值
	map[cury][curx] = ZHUJUE;						// 现在所在的位置为2,
	PrintMap(0);								// 递归函数执行时,当前的地图值
	// 上下左右四个方向探索,并传入当前位置和当前的地图值(以便在下一个递归函数把当前主角所占的位置变回地图值)
	DFS(cury, curx + 1, cury, curx, curysval);// 先向右边探索,为了符合PPT上的图展示效果,不过应该优先向下探索的。
	DFS(cury + 1, curx, cury, curx, curysval);// 向下边探索
	DFS(cury, curx - 1, cury, curx, curysval);// 向左边探索
	DFS(cury - 1, curx, cury, curx, curysval);// 向上边探索

	if (isFind) {// 找到了出口,现在的路径栈保存了正确的访问路径,退出即可,但是这个路径可能不是最优路径
		return;
	}
	// 4个方向都走完了,无路可走,说明这个位置不能通过,需要返回上一个位置
	// 递归函数结束时,相当于栈pop时的操作
	// 需要把递归函数进入时改变的数值等操作回归原位
	//vis[cury][curx] = 0;						// 当前点没访问过,去掉没影响,但恢复为0可能可以找到最优路径
	pathStack.pop();							// 舍去记录当前位置
	map[cury][curx] = NOPATH;				// 这个位置的值为不能通过
	map[lasty][lastx] = ZHUJUE;				// 上一个所在的位置为2
	PrintMap(0);								// 打印当前的地图值
}

// 根据栈保存的路径值,修改地图值
void UpdateMapToPath() {
	while (!pathStack.empty()) {
		int *pos = pathStack.top();
		pathStack.pop();
		map[*pos][*(pos + 1)] = 2;
	}
}
int main() {
	int val;
	cout << "请输入地图行数和列数:" << endl;
	cin >> m >> n;
	cout << "请输入地图数据:" << endl;
	// 1.根据输入初始化地图
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++)
		{
			cin >> val;
			map[i][j] = val;
		}
	}
	int endy = m - 2, endx = n - 1; // 出口列和行,列是x,y是行
	int curx = 1, cury = 1;			// 主角所在的开始行和列
	map[endy][endx] = EXITS;// 给出口位置标记
	system("cls");// 清空输入

	// 2.深度优先搜索,找到路径
	DFS(cury, curx, cury, curx, map[cury][curx]);

	// 3.根据栈保存的信息,修改地图访问的路径
	cout << "成功找到出口,访问出口路径如下:" << endl;
	UpdateMapToPath();
	PrintMap(1); // 最后打印访问过的路径图

}
/*
地图值
10 10
1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 0 1 0 1
1 0 0 1 0 0 0 1 0 1
1 0 0 0 0 1 1 0 0 1
1 0 1 1 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 1
1 0 1 0 0 0 1 0 0 1
1 0 1 1 1 0 1 1 0 1
1 0 0 0 0 1 0 0 0 1
1 1 1 1 1 1 1 1 1 1

11 10 
1 1 1 1 1 1 1 1 1 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 0 0 1 1 0 0 1 
1 0 1 1 1 0 0 0 0 1 
1 0 0 0 1 0 1 0 0 1 
1 0 1 0 0 0 1 0 0 1 
1 0 1 1 1 0 1 1 0 1 
1 0 0 0 0 1 0 1 0 1 
1 0 1 0 0 0 0 1 0 1	
1 1 1 1 1 1 1 1 1 1	
*/

4. 可视化地图效果和代码

完成黑窗口效果后,想使用可视化界面,于是使用了easyx图形库,很简单的将代码重构成和PPT一样的效果,代码变化不大。
要想运行这个代码,得安装easyx库,可以看这篇CSDN博客EASYX使用教程
请添加图片描述

#include <iostream>
#include <graphics.h>      // 引用图形库头文件
#include <conio.h>
#include <stack>
#include <windows.h>
using namespace std;
// 地图值: 1是墙壁,0是通道,2~5是主角的方向(上下左右),6是出口
#define WALL 1
#define PASS 0
#define UP 2
#define DOWN 3
#define LEFT 4
#define RIGHT 5
#define EXITS 6

// 全局变量
int m = 10, n = 10;		// 行和列 默认为10
int map[100][100];		// 地图数组,先开大一点,根据m、n限定范围
int vis[100][100];		// 是否访问过,0是没访问,-1是访问过了
bool isFind = false;		// 是否找到出口
stack<int(*)> pathStack;// 用STL模板的栈类记录路径

// 加载图片
IMAGE imgs[10];
void LoadImags() {
	loadimage(&imgs[0], L"lu.png", 80, 80);
	loadimage(&imgs[1], L"qiang.png", 80, 80);
	loadimage(&imgs[2], L"u.png", 80, 80);
	loadimage(&imgs[3], L"d.png", 80, 80);
	loadimage(&imgs[4], L"l.png", 80, 80);
	loadimage(&imgs[5], L"r.png", 80, 80);
	loadimage(&imgs[6], L"qiang.png", 80, 80);
}

// 绘制地图
void DrawMap() {
	Sleep(300); // 休眠0.3秒,更新图
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++)
		{
			putimage(j * 80, i * 80, &imgs[map[i][j]]);
		}
	}
}
// 辅助函数,当前方向的对立方向
int GetAnotherDir(int dir) {
	if (dir == 2) {
		return 3;
	}
	if (dir == 3) {
		return 2;
	}
	if (dir == 4) {
		return 5;
	}
	if (dir == 5) {
		return 4;
	}
	return dir;
}
/*
思路:采用深度优先搜索
递归中自带“栈特性”,在进入当前递归函数相当于栈的push,退出当前递归函数相当于栈的pop
那么根据这个特性,实现搜索地图中返回上一个结点的功能。
*/
void DFS(int cury, int curx, int dir, int lasty, int lastx, int ysval) {// cury curx 表示当前点,dir是当前位置的方向, lasty,lastx 表示上一个点,ysval表示上一个点的地图值
	if (isFind) {// 找到了出口,递归栈都返回
		return;
	}
	if (vis[cury][curx] == -1 || map[cury][curx] == 1 || cury >= m || cury < 0 || curx >= n || curx < 0) {// 递归返回条件:这个位置访问过,墙壁,越界,
		return;
	}
	if (map[cury][curx] == EXITS) {// 递归退出条件,找到了出口
		// cout << "找到了出口" << endl;
		isFind = true;
		return;
	}

	// 递归函数执行时,执行相当于栈push时的操作
	pathStack.push(new int[3]{ cury, curx, dir });// 记录当前路径和方向
	vis[cury][curx] = -1;				   // 表示访问过当前点
	int curysval = map[cury][curx];			// 保存当前的地图值
	// 更新主角所在位置。
	map[lasty][lastx] = ysval;				// 上一个所在的位置变回地图值
	map[cury][curx] = dir;					// 主角现在所在的位置 = 主角面向方向值
	DrawMap();								// 递归函数执行时,当前的地图值
	// 上下左右四个方向探索,并传入当前位置和当前的地图值(以便在下一个递归函数把当前主角所占的位置变回地图值)
	DFS(cury + 1, curx, DOWN, cury, curx, curysval);// 先向下边探索,这里先往下搜索,对比一下先向右边探索
	DFS(cury, curx + 1, RIGHT, cury, curx, curysval);// 向右边探索
	DFS(cury, curx - 1, LEFT, cury, curx, curysval);// 向左边探索
	DFS(cury - 1, curx, UP, cury, curx, curysval);// 向上边探索

	if (isFind) {// 找到了出口,现在的路径栈保存了正确的访问路径,退出即可,但是这个路径可能不是最优路径
		return;
	}
	// 4个方向都走完了,无路可走,需要返回上一个位置
	// 递归函数结束时,相当于栈pop时的操作
	// 需要把递归函数进入时改变的数值等操作回归原位
	//vis[cury][curx] = 0;						// 当前点没访问过,去掉没影响,但恢复为0可能可以找到最优路径
	pathStack.pop();							// 舍去记录当前位置
	map[cury][curx] = curysval;				// 现在所在的位置变回一开始的地图值
	map[lasty][lastx] = GetAnotherDir(dir);	// 主角返回到上一个位置,上一个位置的方向,是当前对立的方向才能绘图正确
	DrawMap();								// 绘制当前的地图值
}
// 根据栈保存的路径值,修改地图值
void UpdateMapToPath() {
	while (!pathStack.empty()) {
		int* pos = pathStack.top();
		pathStack.pop();
		map[*pos][*(pos + 1)] = *(pos + 2); // 变成主角当时的方向值
	}
}
int main()
{
	int val;
	cout << "请输入地图行数和列数:" << endl;
	cin >> m >> n;
	cout << "请输入地图数据:" << endl;
	// 1.根据输入初始化地图
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++)
		{
			cin >> val;
			map[i][j] = val;
		}
	}
	int endy = m - 2, endx = n - 1; // 出口列和行,列是x,y是行
	int curx = 1, cury = 1;			// 主角开始行和列
	map[endy][endx] = EXITS;			// 给出口位置标记
	
	initgraph(n * 80, m * 80);		// 创建绘图窗口,大小为 n * 80,m * 80 像素
	LoadImags();
	
	// 2.深度优先搜索,找到路径
	DFS(cury, curx, DOWN, cury, curx, map[cury][curx]);

	// 3.根据栈保存的信息,修改地图访问的路径
	UpdateMapToPath();
	DrawMap();

	_getch();              // 按任意键继续
	closegraph();          // 关闭绘图窗口
}
/*
地图值
10 10 
1 1 1 1 1 1 1 1 1 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 0 0 1 1 0 0 1 
1 0 1 1 1 0 0 0 0 1 
1 0 0 0 1 0 0 0 0 1 
1 0 1 0 0 0 1 0 0 1 
1 0 1 1 1 0 1 1 0 1 
1 0 0 0 0 1 0 0 0 1 
1 1 1 1 1 1 1 1 1 1 

11 10 
1 1 1 1 1 1 1 1 1 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 0 0 1 1 0 0 1 
1 0 1 1 1 0 0 0 0 1 
1 0 0 0 1 0 1 0 0 1 
1 0 1 0 0 0 1 0 0 1 
1 0 1 1 1 0 1 1 0 1 
1 0 0 0 0 1 0 1 0 1 
1 0 1 0 0 0 0 1 0 1 
1 1 1 1 1 1 1 1 1 1
*/

5.最后

你当我老师,你给我打几分。。。
请添加图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐