描述
小青蛙有一天不小心落入了一个地下迷宫,小青蛙希望用自己仅剩的体力值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变成了通路,出口变成了右上角,最重要的是路径的改变,不一定只有唯一路径,需要找出最短路径。
先看第一个优化,就是路径的改变,这里从上次的0可以走变成了1可以走,所以判断通路的时候,只用把前面的0变成1就可以了。
bool IsMazePath(int** maze, int n, int m, Pos cur) {
if (cur.row >= 0 && cur.row < n && cur.col >= 0 && cur.col < m &&
maze[cur.row][cur.col] == 1) {
return true;
}
else {
return false;
}
}
接着看体力值和出口的优化,体力值我们在迷宫的函数里增加一个p变量存体力值就可以了,上走-3,左右走-1,这里传值就可以了,不需要传址,比如走到死路的时候,走到这里或许会消耗体力值,我们还需要向上传递回去,这时候并不需要用走过的体力值,所以直接穿值就可以了,出口直接改变成右上角就可以了。
void GetMaze(int** maze, int n, int m, Pos cur,int p ) {
STPush(&path,cur);
if (cur.col == m-1 && cur.row == 0)
{
if(p>=0)
{
}
}
Pos next;
maze[cur.row][cur.col] = 2;
next = cur;
next.row -= 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p-3);
}
next = cur;
next.row += 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p);
}
next = cur;
next.col += 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p-1);
}
next = cur;
next.col -= 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p-1);
}
STPop(&path,cur);
}
接着是最短路径的优化,要找到最短路径,那我们就需要找到所有的路径,然后依次比较就可以了,既然要走所有的路径,我们就不需要返回值了,是通路就走,才可以走完所有的路,此时我们需要新创一个栈来存最短路径,记为minpath,每次走完就需要跟path对比一下,如果是第一次,minpath为空时,直接将path拷贝过去,如果不是,比较之后,path如果小了,就可以拷。
然后是拷贝的问题,拷贝不可以直接minpath = path,如果是这个样子的话,就相当于minpath和path指向了同一块空间,如果再对path进行pop或push,那就不得了了。所以我们需要新写一个函数来完成拷贝,将minpath中的数组开辟和path中的数组开辟一样的大小,然后调用memcpy就可以直接拷贝了。在拷贝之前需要destory一下minpath。
if (cur.col == m-1 && cur.row == 0)
{
if(p>=0&&(STEmpty(&minpath)||STSize(&path)<STSize(&minpath)))
{
STDestory(&minpath);
STCopy(&path,&minpath);
}
}
void STCopy(ST* path,ST* minpath)
{
minpath->a = (SlDataType*)malloc(sizeof(SlDataType)*path->capacity);
memcpy(minpath->a,path->a,sizeof(SlDataType)*path->top);
minpath->capacity = path->capacity;
minpath->top = path->top;
}
接着考虑如何走完所有的路,刚开始我们一进去就把走过的路设置成了2,但是如果有多种路,我们还需要再回去,回去之后如果还是2,那不就走不了了,所以我们需要在最后将2恢复成通路就可以了。下面画一下一个简单的用例(这里为了方便起见,就不考虑体力值了)。
4 4
1 0 0 1
1 1 1 1
1 0 1 1
1 0 0 0
以这个为例,顺序还是上下左右
第一次(上x,下√,左,右)
2 0 0 1
1 1 1 1
1 0 1 1
1 0 0 0
(被第一次的下调用)第二次(上x,下√,左,右)
2 0 0 1
2 1 1 1
1 0 1 1
1 0 0 0
(被第二次的下调用)第三次(上x,下√,左,右)
2 0 0 1
2 1 1 1
2 0 1 1
1 0 0 0
(被第三次的下调用)第四次(上x,下x,左x,右x):走不通,传回到第三次的下
2 0 0 1
2 1 1 1
2 0 1 1
2 0 0 0
(被第四次传递回去)第三次(上x,下√,左x,右x):走不通,传回到第二次的下
2 0 0 1
2 1 1 1
2 0 1 1
1 0 0 0
(被第三次传递回去)第二次(上x,下√,左x,右√)
2 0 0 1
2 1 1 1
1 0 1 1
1 0 0 0
(被第二次的右调用)第五次(上x,下x,左x,右√)
2 0 0 1
2 2 1 1
1 0 1 1
1 0 0 0
(被第五次的右调用)第六次(上x,下√,左,右)
2 0 0 1
2 2 2 1
1 0 1 1
1 0 0 0
(被第六次的下调用)第七次(上x,下x,左x,右√)
2 0 0 1
2 2 2 1
1 0 2 1
1 0 0 0
(被第七次的右调用)第八次(上√,下,左,右)
2 0 0 1
2 2 2 1
1 0 2 2
1 0 0 0
(被第八次的上调用)第九次(上√,下,左,右)
2 0 0 1
2 2 2 2
1 0 2 2
1 0 0 0
(被第九次的上调用)第十次(上x,下x,左x,右x):到达终点,回去
2 0 0 2
2 2 2 2
1 0 2 2
1 0 0 0
(被第十次传递回去)第九次(上√,下x,左x,右x)
2 0 0 1
2 2 2 2
1 0 2 2
1 0 0 0
(被第九次传递回去)第八次(上√,下x,左x,右x)
2 0 0 1
2 2 2 1
1 0 2 2
1 0 0 0
(被第八次传递回去)第七次(上x,下x,左x,右√)(注意,这里已经走过了,继续回调)
2 0 0 1
2 2 2 1
1 0 2 1
1 0 0 0
(被第七次传递回去)第六次(上x,下√,左x,右√)
2 0 0 1
2 2 2 1
1 0 1 1
1 0 0 0
(被第六次的左调用)第十一次(上x,下√,左x,右√)
2 0 0 1
2 2 2 2
1 0 1 1
1 0 0 0
(被第十一次的右调用)第十二次(上x,下x,左x,右x),通路,回去
2 0 0 2
2 2 2 2
1 0 1 1
1 0 0 0
(被第十二次传递回去)第十一次(上x,下√,左x,右√)继续回
2 0 0 1
2 2 2 2
1 0 1 1
1 0 0 0
(被第十一次传递回去)第六次(上x,下√,左x,右√)继续回
2 0 0 1
2 2 2 1
1 0 1 1
1 0 0 0
(被第六次传递回去)第五次(上x,下x,左x,右√)继续回
2 0 0 1
2 2 1 1
1 0 1 1
1 0 0 0
(被第五次传递回去)第二次(上x,下√,左x,右√)继续回
2 0 0 1
1 1 1 1
1 0 1 1
1 0 0 0
(被第二次传递回去)第一次(上x,下√,左x,右x)结束
1 0 0 1
1 1 1 1
1 0 1 1
1 0 0 0
下面是AC代码
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
typedef struct Position {
int row;
int col;
} Pos;
typedef Pos SlDataType;
typedef struct Stack
{
SlDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* pst);
void STDestory(ST* pst);
void STPush(ST* pst, SlDataType x);
void STPop(ST* pst);
SlDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
void STDestory(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = 0;
pst->top = 0;
}
void STPush(ST* pst, SlDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
if (pst->capacity == 0)
pst->capacity = 4;
else
pst->capacity *= 2;
SlDataType* tmp = (SlDataType*)realloc(pst->a, pst->capacity * sizeof(SlDataType));
pst->a = tmp;
}
pst->a[pst->top] = x;
(pst->top)++;
}
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
SlDataType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{
assert(pst);
if (pst->top == 0)
return true;
return false;
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
void STCopy(ST* path,ST* minpath)
{
minpath->a = (SlDataType*)malloc(sizeof(SlDataType)*path->capacity);
memcpy(minpath->a,path->a,sizeof(SlDataType)*path->top);
minpath->capacity = path->capacity;
minpath->top = path->top;
}
ST path;
ST minpath;
bool IsMazePath(int** maze, int n, int m, Pos cur) {
if (cur.row >= 0 && cur.row < n && cur.col >= 0 && cur.col < m &&
maze[cur.row][cur.col] == 1) {
return true;
}
else {
return false;
}
}
void GetMaze(int** maze, int n, int m, Pos cur,int p ) {
STPush(&path,cur);
if (cur.col == m-1 && cur.row == 0)
{
if(p>=0&&(STEmpty(&minpath)||STSize(&path)<STSize(&minpath)))
{
STDestory(&minpath);
STCopy(&path,&minpath);
}
}
Pos next;
maze[cur.row][cur.col] = 2;
next = cur;
next.row -= 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p-3);
}
next = cur;
next.row += 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p);
}
next = cur;
next.col += 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p-1);
}
next = cur;
next.col -= 1;
if (IsMazePath(maze, n, m, next)) {
GetMaze(maze, n, m, next,p-1);
}
STPop(&path);
maze[cur.row][cur.col] = 1;
}
void Print(ST* path)
{
ST rpath;
STInit(&rpath);
while(!STEmpty(path))
{
Pos pt = STTop(path);
STPush(&rpath,pt);
STPop(path);
}
while(STSize(&rpath)>1)
{
Pos mz = STTop(&rpath);
printf("[%d,%d],",mz.row,mz.col);
STPop(&rpath);
}
Pos mz = STTop(&rpath);
printf("[%d,%d]",mz.row,mz.col);
STPop(&rpath);
STDestory(&rpath);
}
int main() {
int a, b,p;
while (scanf("%d %d %d", &a, &b,&p) != EOF) {
int** maze = (int**)malloc(sizeof(int*) * a);
for (int i = 0; i < a; i++)
maze[i] = (int*)malloc(sizeof(int) * b);
for (int i = 0; i < a; i++) {
for (int j = 0; j < b; j++) {
scanf("%d", &maze[i][j]);
}
}
STInit(&path);
STInit(&minpath);
Pos ps = { 0, 0 };
GetMaze(maze, a, b, ps,p);
if(!STEmpty(&minpath))
Print(&minpath);
else
printf("Can not escape!");
for (int i = 0; i < a; i++)
{
free(maze[i]);
maze[i] = NULL;
}
free(maze);
maze = NULL;
}
return 0;
}