【刷题之路Ⅱ】迷宫问题升级版——找最短路径

news2024/11/24 20:43:36

【刷题之路Ⅱ】迷宫问题升级版——找最短路径

  • 一、题目描述
  • 二、解题
    • 1、方法1——暴力递归+更新栈
      • 1.1、思路分析
      • 1.2、先将栈实现以下
      • 1.3、代码实现

一、题目描述

原题连接: 地下迷宫
题目描述:

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

二、解题

其实这题和上一题[迷宫问题]的主要区别就在于增加了存在多条路径和体力值的设定。所以我们主要的代码就可以直接复用上一题代码,然后再将这些设定补全即可。

1、方法1——暴力递归+更新栈

1.1、思路分析

如上所述,我们主要要解决的就是两个问题:体力值和找到最短路径。
先来看看比较简单的体力问题:
对于体力值的解决方法是比较简单的,因为是要移动才要消耗体力值,所以对于体力值的处理当然是放在找通路函数GetPath里,我们只需要在对应的方向处理对应的体力消耗即可。
**有的朋友可能会这么想:**那我可以传一个体力值的指针进GetPath函数,然后在走向对应的方向时通过指针来对外部的体力值进行加减,而如果需要回溯的话再将体力值加回来啊。
其实没必要这样,而且每次都传入一个体力值的指针变量也怪麻烦的。
我们其实可以灵活地运用递归算法的“回归性”,然后只用局部变量的体力值就解决这个问题。我们知道递归其实是层层调用的关系,在某一次调用结束时也并不是直接返回到最外层而是返回到上一层调用处。
所以我们将体力值设置成局部变量,在递归返回的时候,这个临时变量也不会发生改变,也就相当于恢复了原有的体力值了。
所以我们最终四个方向的递归就可以如下面这样设计:

pos next = { 0, 0 };

    next = cur;
    // 上
    next.row += 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power - 3);
    }

    next = cur;
    // 下
    next.row -= 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power);
    }

    next = cur;
    // 左
    next.col -= 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power - 1);
    }

    next = cur;
    // 右
    next.col += 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power - 1);
    }

直接通过形参的方式来控制,其实也和局部变量差不多的。

然后就是比较复杂一点的最短路径的处理:
因为我们并不能知道具体那一次找到的通路是最短路径,所以很明显仅使用一个栈是完成不了任务的。我们还需要额外创建一个栈minPathStack来保存当前找到的最短路径,如果后面在找到了更短的路径,就更新minPathStack中的路径。
而在具体的递归中,因为我们现在是可能存在多条路径可走,所以即使在某个方向的递归中已经找到了通路我们还是得继续递归其他的方向,以找到更优的通路,所以我们就要将GetPath函数设置成不带返回值的:正如上面的代码所示的:
在这里插入图片描述
最后还有一点需要注意的是: 也是因为可能存在多条通路,所以我们在某一条路上头不通或者体力消耗尽了。当我们往回返的时候还需要将这些走过的坐标再改成可通行的坐标,就如下面的这个用红圈圈起来的坐标:
在这里插入图片描述
我们发现红色和绿色的两条路都经过了这个坐标,而红色的那条是走不通的(体能不够),而如果我们在红色的那条路上往回返的时候没有吧走过的坐标再改成可通行的坐标,那到后面在走绿色那条路的时候就没法在通过红圈圈的这个坐标了,那我们也就找不到可行的通路了。

好了,这就是这些就是这一题的重点思路了,剩下的就是代码实现了。

1.2、先将栈实现以下

C选手的第一步当然是自己造轮子啦:

// 定义一个坐标结构体
typedef struct position {
    int row;
    int col;
} pos;
// 先要将栈实现一下
// 重定义数据类型
typedef pos DataType;

// 定义栈结构
typedef struct stack {
    DataType* data;
    int top;
    int capacity;
} Stack;

// 栈的初始化
void StackInit(Stack* ps);

// 压栈
void StackPush(Stack* ps, DataType x);
// 弹栈
void StackPop(Stack* ps);
// 返回栈顶数据
DataType StackTop(Stack* ps);
// 返回栈的数据个数
int StackSize(Stack* ps);
// 判断栈是否为空
bool StackEmpty(Stack* ps);
// 栈的销毁
void DestroyStack(Stack* ps);

// 栈的初始化
void StackInit(Stack* ps) {
    assert(ps);
    ps->data = NULL;
    ps->top = 0;
    ps->capacity = 0;
}

// 压栈
void StackPush(Stack* ps, DataType x) {
    assert(ps);
    // 检查是否需要增容
    if (ps->top == ps->capacity) {
        int newCapacity = ps->capacity == 0 ? 10 : ps->capacity * 2;
        DataType* temp = (DataType*)realloc(ps->data, newCapacity * sizeof(DataType));
        if (NULL == temp) {
            perror("ralloc fail!\n");
            exit(-1);
        }
        ps->data = temp;
        ps->capacity = newCapacity;
    }
    ps->data[ps->top] = x;
    ps->top++;
}

// 弹栈
void StackPop(Stack* ps) {
    assert(ps);
    assert(ps->top > 0);
    ps->top--;
}

// 返回栈顶数据
DataType StackTop(Stack* ps) {
    assert(ps);
    assert(!StackEmpty(ps));
    return ps->data[ps->top - 1];
}

// 返回栈的数据个数
int StackSize(Stack* ps) {
    assert(ps);
    assert(ps->top >= 0);
    return ps->top;
}

// 判断栈是否为空
bool StackEmpty(Stack* ps) {
    assert(ps);
    return ps->top == 0;
}

// 栈的销毁
void DestroyStack(Stack* ps) {
    assert(ps);
    free(ps->data);
    ps->data = NULL;
    ps->top = 0;
    ps->capacity = 0;
}

1.3、代码实现

main函数:
main函数当中我们其实只需要改一下输入的数据增加一个体能值即可,其他的都是调用其他的函数解决。

int main() {
    int Row = 0;
    int Col = 0;
    int power = 0;
    while (scanf("%d %d %d", &Row, &Col, &power) != EOF) {
        int** path = (int**)malloc(Row * sizeof(int*));
        if (NULL == path) {
            perror("malloc fail!\n");
            exit(-1);
        }
        int i = 0;
        for (i = 0; i < Row; i++) {
            path[i] = (int*)malloc(Col * sizeof(int));
            if (NULL == path[i]) {
                perror("malloc fail!\n");
                exit(-1);
            }
        }
        int j = 0;
        for (i = 0; i < Row; i++) {
            for (j = 0; j < Col; j++) {
                scanf("%d", &path[i][j]);
            }
        }
        pos entry = { 0, 0 };
        GetPath(path, Row, Col, entry, power);
        if (!StackEmpty(&minPathStack)) {
            printPath();
        }
        else {
            printf("Can not escape!\n");
        }
    }
    DestroyStack(&pathStack);
    DestroyStack(&minPathStack);
}

GetPath函数:

void GetPath(int** path, int row, int col, pos cur, int power) {
    assert(path);
    StackPush(&pathStack, cur);
    if (cur.row == 0 && cur.col == col - 1) {
        if ((power >= 0 && StackEmpty(&minPathStack)) ||
            (power >= 0 && StackSize(&pathStack) < StackSize(&minPathStack))) {
            // 先销毁原来的minPathStack
            DestroyStack(&minPathStack);

            StackInit(&minPathStack);
            // 将pathStack中的数据拷贝到minPathStack
            int i = 0;
            for (i = 0; i < StackSize(&pathStack); i++) {
                StackPush(&minPathStack, pathStack.data[i]);
            }
        }
    }
    path[cur.row][cur.col] = 2;

    // 判断当前坐标的上下左右四个方向是否能走

    pos next = { 0, 0 };

    next = cur;
    // 上
    next.row += 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power - 3);
    }

    next = cur;
    // 下
    next.row -= 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power);
    }

    next = cur;
    // 左
    next.col -= 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power - 1);
    }

    next = cur;
    // 右
    next.col += 1;
    if (isPass(path, row, col, next)) {
        GetPath(path, row, col, next, power - 1);
    }
    // 将坐标再次改成可通行坐标
    path[cur.row][cur.col] = 1;
    StackPop(&pathStack);
}

判断坐标是否可行:

bool isPass(int** path, int row, int col, pos cur) {
    assert(path);
    if ((cur.row >= 0 && cur.row < row)
        && (cur.col >= 0 && cur.col < col)
        && path[cur.row][cur.col] == 1) {
        return true;
    }
    return false;
}

打印通路:
打印通路我们只需要将打印的栈改成minPathStack再更改一下打印的格式即可:

void printPath() {
    Stack RPathStack;
    StackInit(&RPathStack);

    // 将PathStack栈中的数据全都压入RPathStack栈中
    while (!StackEmpty(&minPathStack)) {
        StackPush(&RPathStack, StackTop(&minPathStack));
        StackPop(&minPathStack);
    }

    pos cur = { 0, 0 };
    // 再将RPathStack栈中的数据取出来打印
    while (!StackEmpty(&RPathStack)) {
        cur = StackTop(&RPathStack);
        StackPop(&RPathStack);
        printf("[%d,%d]", cur.row, cur.col);
        if (!StackEmpty(&RPathStack)) {
            printf(",");
        }
    }
    DestroyStack(&RPathStack);
}

其实这一题的难度已经到达了中等偏上的难度,这种难度的题对你的代码练习量和代码的控制能力是有很高的要求的,里面的各种细节和问题也并非是一篇简单的题解能够说清楚的。
所以最好的建议还是自己动手多刷几遍(当然我不建议大家用C语言来刷……)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/585064.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言深度解析--操作符

目录 操作符 1.算数操作符 2.移位操作符 左移操作符<<&#xff1a; 右移操作符>>&#xff1a; 3.位操作符 按位与&&#xff1a; 按位或 | &#xff1a; 按位异或 ^ &#xff1a; 4.赋值操作符 5.单目操作符 6.关系操作符 7.逻辑操作符 8.条件操作…

这所985复试竟可直接加50分,若复试有科研经历!

本期为大家整理热门院校-“大连理工大学”的择校分析&#xff0c;这个择校分析专题会为大家结合&#xff1a;初试复试占比、复试录取规则&#xff08;是否公平&#xff09;、往年录取录取名单、招生人数、分数线、专业课难度等进行分析。希望能够帮到大家! –所有数据来源于研…

vue:实现简单的拖拽功能

背景 平常做业务很容易遇到拖拽功能&#xff0c;没做之前总觉得会很复杂&#xff0c;今天来看一下到底是怎么实现的。 拖拽API 这是 HTML5 新增的 API&#xff0c;当给元素设置 draggable"true" 的时候&#xff0c;这个元素就可以拖拽了。 <div draggable&quo…

JavaScript全解析——Express框架介绍与入门

本文为千锋资深前端教学老师带来的【JavaScript全解析】系列&#xff0c;文章内含丰富的代码案例及配图&#xff0c;从0到1讲解JavaScript相关知识点&#xff0c;致力于教会每一个人学会JS&#xff01; 文末有本文重点总结&#xff0c;可以收藏慢慢看~ 更多技术类内容&#xf…

Linux 安装nodejs、npm、yarn、nrm(超实用)

前言&#xff1a;初衷想要本地通过dockerfile文件直接把项目打包到linux服务器&#xff0c;不用再本地加载再上传等&#xff0c;后续再贴上配置文件 一、什么是nodejs 来自官网的介绍&#xff0c;Node.js 是一个开源的跨平台 JavaScript 运行时环境。它几乎是任何类型项目的流…

AI加持,Fabric让Power BI生态更强大

在Microsoft Build 2023上微软正式推出了Microsoft Faric预览版&#xff0c;它将Power BI、Azure Synapse、Azure Data Factory的优点整合到了一个统一的SasS服务中。数据工程师、数仓工程师、数据科学家、数据分析师和业务用户可以在Fabric中无缝协作&#xff08;微软这是要卷…

MinIO:基于Go实现的高性能、兼容S3协议的对象存储

High Performance Object Storage for AI 译文&#xff1a;MinIO是一个基于Go实现的高性能、兼容S3协议的对象存储 文档 项目地址&#xff1a;https://github.com/minio/minio官网地址&#xff1a;https://min.io/文档地址&#xff1a;https://docs.min.io/Software Developme…

PyTorch-优化器以及网络模型的修改

目的&#xff1a;优化器可以将神经网络中的参数根据损失函数和反向传播来进行优化&#xff0c;以得到最佳的参数值&#xff0c;让模型预测的更准确。 1. SGD import torch import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flat…

Spring Security 笔记

在Spring Security 5.7.0-M2&#xff0c;我们弃用了 WebSecurityConfigurerAdapter &#xff0c;因为我们鼓励用户转向使用基于组件的安全配置。 为了帮助大家熟悉这种新的配置风格&#xff0c;我们编制了一份常见用例表和推荐的新写法。 配置HttpSecurity Configuration pu…

重磅发布!面向装备制造业服务化转型白皮书

《面向装备制造业服务化转型白皮书》 关于白皮书 《面向装备制造业服务化转型白皮书》通过调研160余家装备制造企业的服务化路径及模式&#xff0c;研讨支持企业开展服务型制造的系统化方案&#xff0c;希望为装备制造业服务化转型&#xff0c;探索切实有效的路径以供参考。 …

Web 自动化测试案例——关闭某视频网站弹出广告以及打开登录框输入内容

文章目录 &#x1f4cb;前言&#x1f3af;自动化测试&#x1f9e9;环境的搭建 &#x1f3af;案例介绍&#x1f4dd;最后 &#x1f4cb;前言 人生苦短&#xff0c;我用Python。许久没写博客了&#xff0c;今天又是久违的参与话题的讨论&#xff0c;话题的内容是&#xff1a;如何…

4.文件系统

组成 Linux&#xff1a;一切皆文件 索引节点&#xff08;I-node&#xff09; I-node&#xff08;Index Node&#xff09;&#xff1a;文件系统的内部数据结构&#xff0c;用于管理文件的元数据和数据块。 文件的元数据&#xff1a;包括文件的权限、拥有者、大小、时间戳、索引…

VM增加磁盘并挂载到根目录

1、虚拟机增加磁盘 首先要关闭虚拟机&#xff0c;否则增加按钮不可见。 9 vm添加磁盘完毕。 2、登录虚拟机挂盘 1、lsblk查看硬盘挂载情况&#xff0c;sdb为新挂载的磁盘。 [rootlocalhost ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda …

通过python封装接口采集1688店铺所有商品数据接口,1688店铺所有商品接口,1688API接口

采集1688店铺所有商品数据需要进行以下步骤&#xff1a; 获取店铺ID 要获取店铺ID&#xff0c;您可以通过访问店铺首页来获取&#xff0c;例如&#xff1a;https://1688455341.1688.com/ 店铺ID就是链接中的“1688455341”。 获取店铺所有商品列表页 通过向1688店铺的搜索…

关于【SD-WEBUI】的LoRA模型训练:怎样才算训练好了?

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;模型(LoRA)训练&#xff08;1.1&#xff09;数据准备&#xff08;1.1.1&#xff09;筛选照片&#xff08;1.1.2&#xff09;预处理照片&#xff08;1.1.3&#xff09;提示词(tags)处理&#xff08;1.1.4&…

部署微信小程序-shopro

部署微信小程序 开始之前 注意不要运行模式下的代码提交小程序审核&#xff0c;第一包体积太大&#xff0c;第二性能太差请下载 小程序开发工具正式小程序无法正常使用&#xff0c;而开发版正常&#xff0c;请确保域名都添加到小程序后台&#xff0c;并且配置好了 IP 白名单&a…

Openai+Deeplearning.AI: ChatGPT Prompt Engineering(五)

想和大家分享一下最近学习的Deeplearning.AI和openai联合打造ChatGPT Prompt Engineering在线课程.以下是我写的关于该课程的前四篇博客&#xff1a; ChatGPT Prompt Engineering(一)ChatGPT Prompt Engineering(二)ChatGPT Prompt Engineering(三)ChatGPT Prompt Engineering…

微星笔记本618大促至高直降5000元,泰坦GP78 HX爆款配置10999拿下

在万众玩家的期待下&#xff0c;微星笔记本618大促如约而至&#xff01;不仅覆盖今年全新13代酷睿HX RTX40系显卡的高能游戏本&#xff0c;还特别在618同步推出新品&#xff1a;泰坦GP78 HX&#xff0c;承袭“泰坦系列”旗舰的满血基因极致性能体验外&#xff0c;更有i9-13980…

自学web前端能找到工作吗?是否有必要参加前端培训?

是的&#xff0c;自学前端可以帮助您找到工作&#xff0c;参加培训是根据个人学习能力和经济实力来自己决定的。前端开发是一个相对容易入门的领域&#xff0c;并且许多人通过自学成功地找到了前端开发的工作。以下是好程序员的一些建议&#xff0c;可以帮助您在自学前端时提高…

头顶“米链代工厂”标签,德尔玛上市之后怎么走?

截至5月29日上午收盘&#xff0c;德尔玛股价当前为14.10、成交量55272手、成交额为7820.32万&#xff0c;总市值65.08亿元&#xff0c;总股本为4.62亿。 曲折的股价走势背后&#xff0c;德尔玛未来的增长潜力成疑。德尔玛表示&#xff0c;此次上市将有助于公司在创新家电市场保…