C语言地下迷宫

本文介绍了一种算法,用于解决小青蛙在地下迷宫中,给定剩余体力值P,寻找从起点到右上角出口的最短路径。通过判断路径、体力消耗和出口位置的优化,以及使用栈来存储和比较路径,以确定小青蛙是否能逃离迷宫并找到最优路径。
摘要由CSDN通过智能技术生成

描述

小青蛙有一天不小心落入了一个地下迷宫,小青蛙希望用自己仅剩的体力值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 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

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

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 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 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;
}

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值