感谢各位大佬的光临,希望和大家一起进步,望得到你的三连,互三支持,一起进步
前言
我们接着之前讲过的初阶迷宫问题,这次我们来说说高阶迷宫问题,高阶的迷宫问题难在有多条路径去选择,所以我们现在需要做的不是找到路径而是找到最短的路径
地下迷宫_牛客题霸_牛客网 (nowcoder.com)
小青蛙有一天不小心落入了一个地下迷宫,小青蛙希望用自己仅剩的体力值P跳出这个地下迷宫。为了让问题简单,假设这是一个n*m的格子迷宫,迷宫每个位置为0或者1,0代表这个位置有障碍物,小青蛙达到不了这个位置;1代表小青蛙可以达到的位置。小青蛙初始在(0,0)位置,地下迷宫的出口在(0,m-1)(保证这两个位置都是1,并且保证一定有起点到终点可达的路径),小青蛙在迷宫中水平移动一个单位距离需要消耗1点体力值,向上爬一个单位距离需要消耗3个单位的体力值,向下移动不消耗体力值,当小青蛙的体力值等于0的时候还没有到达出口,小青蛙将无法逃离迷宫。现在需要你帮助小青蛙计算出能否用仅剩的体力值跳出迷宫(即达到(0,m-1)位置)。
输入描述:
输入包括n+1行:
第一行为三个整数n,m(3 <= m,n <= 10),P(1 <= P <= 100)
接下来的n行:
每行m个0或者1,以空格分隔输出描述:
如果能逃离迷宫,则输出一行体力消耗最小的路径,输出格式见样例所示;如果不能逃离迷宫,则输出"Can not escape!"。 测试数据保证答案唯一
示例1
输入:
4 4 10 1 0 0 1 1 1 0 1 0 1 1 1 0 0 1 1复制输出:
[0,0],[1,0],[1,1],[2,1],[2,2],[2,3],[1,3],[0,3]
这个题目和我们之前说的迷宫问题来说,多加了一个体力值的限制条件和最短路径,所以我们还是可以先用我们之前的思路深度优先遍历,四路递归,回溯算法
开始我们肯定还是先定义一个结构体变量
typedef struct Postion
{
int row;
int col;
}PT;
一.主要逻辑
和之前写的一样,利用数组指针开辟二位数组,但是这次要初始化两个栈,因为存在最短路径的比较,每次找一个路径就放一个栈,再次找到就和之前的做比较,然后把短的放小的栈就可以了,因为体力值的概念要注意未完成的改变
int main()
{
int N = 0, M = 0, P = 0;
while (scanf("%d%d%d", &N, &M, &P) != EOF)
{
//创建迷宫,获取迷宫地图
// int a[n][m]; // vs2022 不支持
// 动态开辟二维数组
int** maze = (int**)malloc(sizeof(int*) * N);
for (int i = 0; i < N; ++i)
{
maze[i] = (int*)malloc(sizeof(int) * M);
}
// 二维数组的输入
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < M; ++j)
{
scanf("%d", &maze[i][j]);
}
}
StackInit(&path);
StackInit(&minpath);
// PrintMaze(maze, N, M);
PT entry = { 0, 0 }; // 默认[0,0]是入口,[N-1][M-1]是出口
GetMazePath(maze, N, M, entry, P); //获取遍历到的有效路径
if (!StackEmpty(&minpath))
{
PirntPath(&minpath);
}
else
{
printf("Can not escape!\n");
}
StackDestory(&path);
StackDestory(&minpath);
//销毁迷宫
for (int i = 0; i < N; ++i)
{
free(maze[i]);
}
free(maze);
maze = NULL;
}
return 0;
}
二.走迷宫
判断有效性
这次迷宫布局和上次不一样要注意,但是思考的方式是一样的,只要不是墙,并且不出迷宫就都可以走。
bool IsPass(int** maze, int N, int M, PT pos)
{
if (pos.row >= 0 && pos.row < N
&& pos.col >= 0 && pos.col < M
&& maze[pos.row][pos.col] == 1)
{
return true;
}
else
{
return false;
}
}
获取有效便利的路径坐标
便利的方式和之前的一样,但是不会再退出了,而是全部遍历完,所以就要删除之前还是地归正确返回值,不管真不正确都要回来再走一遍,因为递归会回溯,所以探索其他通路,要恢复,因为要探索多条路径,所以手指两个栈进行比较,如果体力值还够的话,并且最小的栈为空或者是栈中的元素小于最小栈,这时候就把栈给复制给小栈
//判断路径坐标进行可以进行遍历
bool GetMazePath(int** maze, int N, int M, PT cur)
{
//直接入栈
StackPush(&path, cur);
// 如果走到出口
if (cur.row == N - 1 && cur.col == M - 1)
return true;
// 探测cur位置得上、下、左、右四个方向
PT next;
maze[cur.row][cur.col] = 2; //已经遍历过的(包括当前)位置,标记为2
//向上遍历
next = cur;
next.row -= 1;
if (IsPass(maze, N, M, next))
{
if (GetMazePath(maze, N, M, next))
return true;
}
//向下遍历
next = cur;
next.row += 1;
if (IsPass(maze, N, M, next))
{
if (GetMazePath(maze, N, M, next))
return true;
}
//向左遍历
next = cur;
next.col -= 1;
if (IsPass(maze, N, M, next))
{
if (GetMazePath(maze, N, M, next))
return true;
}
//向右遍历
next = cur;
next.col += 1;
if (IsPass(maze, N, M, next))
{
if (GetMazePath(maze, N, M, next))
return true;
}
StackPop(&path); //不通,这个坐标不能使用,就出栈
return false; //四个方向都不通,就返回false,递归会回溯,探索其他通路
}
栈的复制
栈的复制,不可以直接赋值,因为如果你直接赋值的话他们就会共用一个地址,在后面销毁站的时候就会销毁两次就会有问题,那如何解决这种浅拷贝问题呢?
直接开辟空间,利用c语言库里的拷贝函数,将栈里的元素拷贝到最小栈中去
//解决浅拷贝的两次释放空间报错问题
void StackCopy(ST* ppath, ST* pcopy)
{
pcopy->a = (STDataType*)malloc(sizeof(STDataType*) * ppath->capacity);
memcpy(pcopy->a, ppath->a, sizeof(STDataType) * ppath->top);
pcopy->top = ppath->top;
pcopy->capacity = ppath->capacity;
}
三.路径坐标的打印
注意观察题目,这个的打印方式和之前的是不一样的,所以我们要重新考虑打印方法
// 输出栈里面的坐标路径
//要求从最开始的坐标先输出,而栈是后进先出,需要调整输出
void PirntPath(ST* ps)
{
// path数据倒到rPath
ST rPath;
StackInit(&rPath);
while (!StackEmpty(ps))
{
StackPush(&rPath, StackTop(ps));
StackPop(ps);
}
//输出分为两种,仅因为题目要求,这里没有什么特定含义
while (StackSize(&rPath) > 1)
{
PT top = StackTop(&rPath);
printf("[%d,%d],", top.row, top.col);
StackPop(&rPath);
}
PT top = StackTop(&rPath);
printf("[%d,%d]", top.row, top.col);
StackPop(&rPath);
StackDestory(&rPath);
}
总代码
注意c语言没有栈,所以做这个题目的时候还要自己去搭建一个栈
//2.迷宫最短路径问题
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<string.h>
//定义迷宫坐标的结构体
typedef struct Postion
{
int row;
int col;
}PT;
//栈的实现
typedef PT STDataType;
typedef struct Stack
{
STDataType* a; //通过数组实现栈的结构
int top;
int capacity;
}ST;
//初始化
void StackInit(ST* ps);
//释放内存、销毁空间
void StackDestory(ST* ps);
// 入栈
void StackPush(ST* ps, STDataType x);
// 出栈
void StackPop(ST* ps);
//取栈顶数据
STDataType StackTop(ST* ps);
//栈的大小
int StackSize(ST* ps);
//判空
bool StackEmpty(ST* ps);
//初始化
void StackInit(ST* ps)
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
printf("malloc fail!\n");
exit(-1);
}
ps->capacity = 4;
ps->top = 0; //这使得top最终指向的是栈顶的后一个位置。若top=-1,则最终指向的是栈顶。
}
//释放内存、销毁空间
void StackDestory(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
// 入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
// 满了->增容
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
if (tmp == NULL)
{
printf("realloc fail!\n");
exit(-1);
}
else
{
ps->a = tmp;
ps->capacity *= 2;
}
}
ps->a[ps->top] = x;
ps->top++;
}
// 出栈
void StackPop(ST* ps)
{
assert(ps);
// 栈空了,再调用Pop,就会直接中止程序报错
assert(ps->top > 0);
//ps->a[ps->top - 1] = 0; //置为0只考虑了int型等,若为char、double等就不适用了。
ps->top--;
}
//取栈顶数据
STDataType StackTop(ST* ps)
{
assert(ps);
// 栈空了,再调用Top,就会直接中止程序报错
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
//求栈大小
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
//判空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//maze实现
ST path; //定义全局变量
ST minpath; //定义最短路径
//打印
void PrintMaze(int** maze, int N, int M)
{
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < M; ++j)
{
printf("%d ", maze[i][j]);
}
printf("\n");
}
printf("\n");
}
// 输出栈里面的坐标路径
//要求从最开始的坐标先输出,而栈是后进先出,需要调整输出
void PirntPath(ST* ps)
{
// path数据倒到rPath
ST rPath;
StackInit(&rPath);
while (!StackEmpty(ps))
{
StackPush(&rPath, StackTop(ps));
StackPop(ps);
}
//输出分为两种,仅因为题目要求,这里没有什么特定含义
while (StackSize(&rPath) > 1)
{
PT top = StackTop(&rPath);
printf("[%d,%d],", top.row, top.col);
StackPop(&rPath);
}
PT top = StackTop(&rPath);
printf("[%d,%d]", top.row, top.col);
StackPop(&rPath);
StackDestory(&rPath);
}
//判断该坐标是否为有效可遍历的坐标
bool IsPass(int** maze, int N, int M, PT pos)
{
if (pos.row >= 0 && pos.row < N
&& pos.col >= 0 && pos.col < M
&& maze[pos.row][pos.col] == 1)
{
return true;
}
else
{
return false;
}
}
//解决浅拷贝的两次释放空间报错问题
void StackCopy(ST* ppath, ST* pcopy)
{
pcopy->a = (STDataType*)malloc(sizeof(STDataType*) * ppath->capacity);
memcpy(pcopy->a, ppath->a, sizeof(STDataType) * ppath->top);
pcopy->top = ppath->top;
pcopy->capacity = ppath->capacity;
}
//获取可有效遍历的路径坐标
void GetMazePath(int** maze, int N, int M, PT cur, int P)
{
StackPush(&path, cur);
// 如果走到出口
if (cur.row == 0 && cur.col == M - 1)
{
// 找到了更短的路径,更新minpath; //P是题目要求的体力值,要求在有效范围内
if (P >= 0 && StackEmpty(&minpath)
|| StackSize(&path) < StackSize(&minpath))
{
StackDestory(&minpath);
StackCopy(&path, &minpath);
}
}
// 探测cur位置得上下左右四个方向
PT next;
maze[cur.row][cur.col] = 2; //已经走过得,标记为2
//向上遍历
next = cur;
next.row -= 1;
if (IsPass(maze, N, M, next))
{
(GetMazePath(maze, N, M, next, P - 3));
}
//向下遍历
next = cur;
next.row += 1;
if (IsPass(maze, N, M, next))
{
(GetMazePath(maze, N, M, next, P));
}
//向左遍历
next = cur;
next.col -= 1;
if (IsPass(maze, N, M, next))
{
(GetMazePath(maze, N, M, next, P - 1));
}
//向右遍历
next = cur;
next.col += 1;
if (IsPass(maze, N, M, next))
{
(GetMazePath(maze, N, M, next, P - 1));
}
//递归会回溯,探索其他通路
//需要恢复
maze[cur.row][cur.col] = 1;
StackPop(&path);
}
int main()
{
int N = 0, M = 0, P = 0;
while (scanf("%d%d%d", &N, &M, &P) != EOF)
{
//创建迷宫,获取迷宫地图
// int a[n][m]; // vs2022 不支持
// 动态开辟二维数组
int** maze = (int**)malloc(sizeof(int*) * N);
for (int i = 0; i < N; ++i)
{
maze[i] = (int*)malloc(sizeof(int) * M);
}
// 二维数组的输入
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < M; ++j)
{
scanf("%d", &maze[i][j]);
}
}
StackInit(&path);
StackInit(&minpath);
// PrintMaze(maze, N, M);
PT entry = { 0, 0 }; // 默认[0,0]是入口,[N-1][M-1]是出口
GetMazePath(maze, N, M, entry, P); //获取遍历到的有效路径
if (!StackEmpty(&minpath))
{
PirntPath(&minpath);
}
else
{
printf("Can not escape!\n");
}
StackDestory(&path);
StackDestory(&minpath);
//销毁迷宫
for (int i = 0; i < N; ++i)
{
free(maze[i]);
}
free(maze);
maze = NULL;
}
return 0;
}
总结
这就是高阶的迷宫问题,初阶的数据结构到这里就全部结束了,以后跟新c++的相关内容