每日一题——地下迷宫(迷宫问题II)

news2024/12/23 13:36:19

迷宫问题(地下迷宫)——II

题目链接

前言:

这题是在昨天迷宫问题——I的基础上进行的变形,因此,如果昨天的题目没看或者对迷宫问题不怎么了解,建议先看看昨天的解析。

迷宫问题——I源代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef struct Position
{
	int row;
	int col;
}POS;

typedef POS StackDataType;
typedef struct Stack
{
	StackDataType* data;
	int top;
}ST;

ST STACK;	//定义一个全局栈

//初始化栈
void StackInit(ST* stack)
{
	stack->top = 0;
	stack->data = (POS*)malloc(sizeof(POS) * 100);
}

//判断栈空
bool StackEmpty(ST* stack)
{
	return stack->top == 0;
}

//入栈
void StackPush(ST* stack, POS val)
{
	stack->data[(stack->top)++] = val;
}

//出栈
void StackPop(ST* stack)
{
	assert(!StackEmpty(stack));

	stack->top--;
}

//返回栈顶元素
StackDataType StackTop(ST* stack)
{
	return stack->data[stack->top - 1];
}

//判断是否可以前进
bool Judge(int** nums, int n, int m, POS pos)
{
	if (pos.col >= m || pos.col < 0 ||
		pos.row >= n || pos.row < 0 ||
		nums[pos.row][pos.col] != 0)
	{
		return false;
	}
	else
		return true;
}

//找路
bool FindWay(int** nums, int n, int m, POS entry)
{
    //先将该位置入栈
	StackPush(&STACK, entry);

    //判断是否已经到了出口
	if(entry.row == n - 1 && entry.col == m - 1)
		return true;

    //为了防止走相同的路,要对走过的路做标记
	nums[entry.row][entry.col] = -1;

	//上
	POS next = entry;
	next.row--;
	if (Judge(nums, n, m, next))
	{
        //如果到了出口,直接返回
		if (FindWay(nums, n, m, next))
			return true;
	}

	//下
	next = entry;
	next.row++;
	if (Judge(nums, n, m, next))
	{
        //如果到了出口,直接返回
		if (FindWay(nums, n, m, next))
			return true;
	}

	//左
	next = entry;
	next.col--;
	if (Judge(nums, n, m, next))
	{
        //如果到了出口,直接返回        
		if (FindWay(nums, n, m, next))
			return true; 
	}

	//右
	next = entry;
	next.col++;
	if (Judge(nums, n, m, next))
	{
        //如果到了出口,直接返回        
		if (FindWay(nums, n, m, next))
			return true;
	}

    //如果是死路,就出栈,并返回假,说明没到出口
	StackPop(&STACK);

	return false;
}

void PrintWay()
{
    //创建一个辅助栈
	ST* stack = (ST*)malloc(sizeof(ST));
	StackInit(stack);

    //将原始栈的数据放入辅助栈
	while (!StackEmpty(&STACK))
	{
		POS temp = StackTop(&STACK);
		StackPop(&STACK);
		StackPush(stack, temp);
	}

    //打印辅助栈的路径
	while (!StackEmpty(stack))
	{
		POS temp = StackTop(stack);
		StackPop(stack);
		printf("(%d,%d)\n", temp.row, temp.col);
	}
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);

    //动态申请二维数组
	int** nums = (int**)malloc(sizeof(int*) * n);
	for (int i = 0; i < n; i++)
	{
		nums[i] = (int*)malloc(sizeof(int) * m);
	}

    //输入
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			scanf("%d", &nums[i][j]);

    //初始化全局栈
	StackInit(&STACK);

    //找路
	POS entry = { 0,0 };
	FindWay(nums, n, m, entry);

    //打印路径
	PrintWay();
    
    //释放动态空间
    for(int i  = 0; i < n; i++)
        free(nums[i]);
    free(nums);

	return 0;
}

思路

总体来说,这题相较于昨天的迷宫问题——I,有以下几点的变化:

  • 可以走的路变成了1,而0是不能走的路
  • 出口由右下角变成了右上角
  • 增加了体力值的概念,如果体力值不够到达出口,则不能逃离迷宫
  • 如果不讨论体力值,那么可能有多条通向出口的路,但如果要看体力值,根据题意,则只存在(或不存在)一条路径,这涉及到了最短路径,因此我们必须要将所有可能的路径都求出来
  • 最后打印路径的格式不同

在迷宫问题——I的基础上修改

由于问题2和问题2障碍物和可以到达的位置所代表的数据恰好相反,首先修改Judge()函数:

bool Judge(int** nums, int n, int m, POS pos)
{
	if (pos.col >= m || pos.col < 0 ||
		pos.row >= n || pos.row < 0 ||
		nums[pos.row][pos.col] != 1)
	{
		return false;
	}
	else
		return true;
}

出口的位置被修改,修改判断是否到达出口的条件:

if(P >= 0 && entry.row == 0 && entry.col == m - 1)
{
    ………………
}

《迷宫问题——I》中,因为有效路径唯一,所以我们找到一条路就可以退出递归,直接打印路径了。而《迷宫问题——II》需要求最短路径,因此我们就必须先找出所有可能的路径,所以我们就不能找到一条路就退出函数,所以我们要将FindWay()的返回类型由bool改为void,到达出口也不要返回,同时要引入体力参数P

void FindWay(int** nums, int n, int m, POS entry, int P)
{
    ………………
}

《迷宫问题——I》中,为避免回溯之后走相同的路,我们会将走过的路进行标记。但是到了《迷宫问题——II》,如果找完一条路后再找第二条路,如果之前的标记没有被消除,那么就不能继续找路了,因此在回溯的过程中,我们要将标记取消。

void FindWay(int** nums, int n, int m, POS entry, int P)
{
	……………………
    //上
	……………………
    //下
    ……………………
    //左
    ……………………  
    //右
	……………………
    nums[entry.row][entry.col] = 1;
}

找最短路径

要找到最短路径,我们只需要再创建一个栈MIN_STACK

  • 如果MIN_STACK为空,那么直接将找到的路径放入MIN_STACK
  • 如果不为空,那么就比较找到的路径的长度和MIN_STACK存储路径的长度,如果新找到的路径要短,就先销毁MIN_STACK中的路径,再将新找到的路径放入
  • 注意,如果到达出口时体力小于0,那么这条路径就是无效路径
if(P >= 0 && entry.row == 0 && entry.col == m - 1)	//如果到达出口且体力值大于等于0
{
    if(StackEmpty(&MIN_STACK) || MIN_STACK.top > STACK.top)	//如果MIN_STACK为空或者原最短路径长度大于新的长度
    {
        StackDestory(&MIN_STACK);	//先销毁原有数据
        StackCopy(&MIN_STACK, &STACK);	//进行数据拷贝
    }
}

FindWay实现

void FindWay(int** nums, int n, int m, POS entry, int P)
{
    //将当前位置入栈
	StackPush(&STACK, entry);
	
    //为防止走相同的路,给当前位置标记
	nums[entry.row][entry.col] = -1;

    if(P >= 0 && entry.row == 0 && entry.col == m - 1)	//如果到达出口且体力值大于等于0
    {
        if(StackEmpty(&MIN_STACK) || MIN_STACK.top > STACK.top)//如果MIN_STACK为空或原最短路径长度大于新的长度
        {
            StackDestory(&MIN_STACK);	//先销毁原有数据
            StackCopy(&MIN_STACK, &STACK);	//进行数据拷贝
        }
    }

	//上
	POS next = entry;
	next.row--;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P - 3);
	}

	//下
	next = entry;
	next.row++;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P);
	}

	//左
	next = entry;
	next.col--;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P - 1);
	}

	//右
	next = entry;
	next.col++;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P - 1);
	}
	
    //无路可走或者已经到达出口就回溯,继续找其他的路
    //将当前位置出栈
	StackPop(&STACK);
	
    //删除标记
    nums[entry.row][entry.col] = 1;
}

拷贝路径(StackCopy)

//将stack2的数据拷贝到stack1中

void StackCopy(ST* stack1, ST* stack2)
{
    stack1->data = (StackDataType *)malloc(sizeof(StackDataType) * stack2->top);	//创建相同大小的数组
    
    memcpy(stack1->data, stack2->data, sizeof(StackDataType) * stack2->top);	//拷贝数组
    
    stack1->top = stack2->top;	//拷贝大小
}

打印路径

void PrintWay()
{
	ST* stack = (ST*)malloc(sizeof(ST));
	StackInit(stack);

	while (!StackEmpty(&MIN_STACK))
	{
		POS temp = StackTop(&MIN_STACK);
		StackPop(&MIN_STACK);
		StackPush(stack, temp);
	}

    //由于最后一个坐标的后面不要打印字符',',因此循环到倒数第二个坐标就停止,最后一个坐标单独打印
	while (stack->top - 1)
	{
		POS temp = StackTop(stack);
		StackPop(stack);
		printf("[%d,%d],", temp.row, temp.col);
	}
    //最后一个坐标单独打印
    POS temp = StackTop(stack);
    StackPop(stack);
    printf("[%d,%d]\n", temp.row, temp.col);
}

实现代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>

typedef struct Position
{
	int row;
	int col;
}POS;

typedef POS StackDataType;
typedef struct Stack
{
	StackDataType* data;
	int top;
}ST;

ST STACK;
ST MIN_STACK;

//初始化栈
void StackInit(ST* stack)
{
	stack->top = 0;
	stack->data = (POS*)malloc(sizeof(POS) * 100);
}

//判断栈空
bool StackEmpty(ST* stack)
{
	return stack->top == 0;
}

//入栈
void StackPush(ST* stack, POS val)
{
	stack->data[(stack->top)++] = val;
}

//出栈
void StackPop(ST* stack)
{
	assert(!StackEmpty(stack));

	stack->top--;
}

//返回栈顶元素
StackDataType StackTop(ST* stack)
{
	return stack->data[stack->top - 1];
}

//销毁栈
void StackDestory(ST *stack)
{
    free(stack->data);
    stack->data = NULL;
    stack->top = 0;
}

//复制栈
void StackCopy(ST* stack1, ST* stack2)
{
    stack1->data = (StackDataType *)malloc(sizeof(StackDataType) * stack2->top);
    memcpy(stack1->data, stack2->data, sizeof(StackDataType) * stack2->top);
    stack1->top = stack2->top;
}

//判断位置是否有效
bool Judge(int** nums, int n, int m, POS pos)
{
	if (pos.col >= m || pos.col < 0 ||
		pos.row >= n || pos.row < 0 ||
		nums[pos.row][pos.col] != 1)
	{
		return false;
	}
	else
		return true;
}

//找路
void FindWay(int** nums, int n, int m, POS entry, int P)
{
    //将当前位置入栈
	StackPush(&STACK, entry);
	
    //为防止走相同的路,给当前位置标记
	nums[entry.row][entry.col] = -1;

    if(P >= 0 && entry.row == 0 && entry.col == m - 1)	//如果到达出口且体力值大于等于0
    {
        if(StackEmpty(&MIN_STACK) || MIN_STACK.top > STACK.top)//如果MIN_STACK为空或原最短路径长度大于新的长度
        {
            StackDestory(&MIN_STACK);	//先销毁原有数据
            StackCopy(&MIN_STACK, &STACK);	//进行数据拷贝
        }
    }

	//上
	POS next = entry;
	next.row--;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P - 3);
	}

	//下
	next = entry;
	next.row++;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P);
	}

	//左
	next = entry;
	next.col--;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P - 1);
	}

	//右
	next = entry;
	next.col++;
	if (Judge(nums, n, m, next))
	{
		FindWay(nums, n, m, next, P - 1);
	}
	
    //无路可走或者已经到达出口就回溯,继续找其他的路
    //将当前位置出栈
	StackPop(&STACK);
	
    //删除标记
    nums[entry.row][entry.col] = 1;
}

//打印路径
void PrintWay()
{
	ST* stack = (ST*)malloc(sizeof(ST));
	StackInit(stack);

	while (!StackEmpty(&MIN_STACK))
	{
		POS temp = StackTop(&MIN_STACK);
		StackPop(&MIN_STACK);
		StackPush(stack, temp);
	}

    //由于最后一个坐标的后面不要打印字符',',因此循环到倒数第二个坐标就停止,最后一个坐标单独打印
	while (stack->top - 1)
	{
		POS temp = StackTop(stack);
		StackPop(stack);
		printf("[%d,%d],", temp.row, temp.col);
	}
    //最后一个坐标单独打印
    POS temp = StackTop(stack);
    StackPop(stack);
    printf("[%d,%d]\n", temp.row, temp.col);
}

int main()
{
	int n, m;
    int P;
	scanf("%d%d%d", &n, &m, &P);
	
    //申请内存
	int** nums = (int**)malloc(sizeof(int*) * n);
	for (int i = 0; i < n; i++)
	{
		nums[i] = (int*)malloc(sizeof(int) * m);
	}
	
    //输入数据
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			scanf("%d", &nums[i][j]);

	StackInit(&STACK);
    StackInit(&MIN_STACK);

	POS entry = { 0,0 };
	FindWay(nums, n, m, entry, P);
	
    //如果找路之后保存最短路径的栈为空,就说明青蛙不能走出迷宫
    if(!StackEmpty(&MIN_STACK))
	    PrintWay();
    else
        printf("Can not escape!\n");

    //释放动态内存
    for(int i  = 0; i < n; i++)
        free(nums[i]);
    free(nums);
    
    StackDestory(&STACK);
    StackDestory(&MIN_STACK);

	return 0;
}

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

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

相关文章

安装redis,适配阿里云服务器,Liunx安装redis

下载redis以及编译安装 下载redis文件 wget http://download.redis.io/releases/redis-6.0.8.tar.gz #下载redis压缩文件 tar xzf redis-6.0.8.tar.gz #解压缩 cd redis-6.0.8 make 查看是否安装了gcc编译输入gcc --version如果没有…

AtcoderABC236场

A - chukodaiA - chukodai 题目大意 题目要求交换字符串S中第a个字符和第b个字符的位置&#xff0c;并输出结果。 思路分析 借用临时变量t&#xff0c;进行记录交换。 时间复杂度 O(∣S∣) 输出交换后的字符串的时间复杂度为O(∣S∣)&#xff0c;其中∣S∣表示字符串SS的…

autohotkey实战:窗口透明化

文章目录 实现方法代码讲解WinGet和WinSet 注 本文采用的是V1版本语法 实现方法 窗口透明不仅实用性强&#xff0c;关键是非常炫酷&#xff0c;如果用AHK实现一个实时调节窗口透明度的工具&#xff0c;那么就可以一边敲代码&#xff0c;一边透过半透明的IDE&#xff0c;愉快地…

vue 3.0 如何实现 文本框只能输入数字 避免文字和符号

<input v-model.number"form.payTime" :min"0" type"number" οninput"valuevalue.replace(/[^0-9.]/g,)" type"text" /> v-model 后面的.numer 一定要加上不然没效果 οninput"valuevalue.replace(/[^0-9.]/g…

Pdb蛋白质数据库网址!+30蛋白质数据库网站!

蛋白质数据库是指专门存储蛋白质相关信息的数据库。它们收集、整理和存储大量的蛋白质数据&#xff0c;包括蛋白质序列、结构、功能、互作关系、表达模式、疾病关联等信息。蛋白质数据库提供了对这些数据的检索、查询和分析功能&#xff0c;为科学研究人员、生物信息学家和药物…

为什么deferred probe将设备挂入延迟链表而不是将驱动挂入延迟链表

1. 代码流程(drivers/base/dd.c) 可以看到在probe失败的时候(驱动返回-EPROBE_DEFER)是把设备挂到deferred_probe_pending_list上面的。 这就带来了一个疑问: 我当前明明是驱动加载的过程(driver_attach()->bus_for_each_dev()), 为什么要将设备挂到pending list上面而不是…

Maven高级(继承与聚合+私服)

分模块设计和开发 为什么要分模块设计&#xff1f; 一个项目往往是分为好几个模块的 如果不同模块全写在一个项目里面 所有的程序员都要调用这有个项目就难以维护 比如我们之前设计的板块 就不太合理&#xff0c;现在我们把实体类和对应的工具类单独开出两个maven模块存储 然…

安装openai和简单使用

Anaconda的界面创建open ai环境&#xff0c;选择python10 控制台 #或者 conda info -e 注意不是anaconda命令开头 (base) C:\Users\su>conda env list # conda environments: # base * F:\anaconda3 openai F:\anaconda3\envs\opena…

TCP的拥塞控制、提高网络利用率的方法【TCP原理(笔记四)】

文章目录 拥塞控制慢启动 提高网络利用率的规范Nagle算法延迟确认应答捎带应答 拥塞控制 有了TCP的窗口控制&#xff0c;收发主机之间即使不再以一个数据段为单位发送确认应答&#xff0c;也能够连续发送大量数据包。然而&#xff0c;如果在通信刚开始时就发送大量数据&#x…

新能源汽车交流充电桩CP信号详解

随着新能源汽车的推广&#xff0c;交流充电桩迎来了巨大的市场需求&#xff0c;人们对车辆充电的便利性、安全性有着越来越高的要求。CP信号主要用于交流充电桩&#xff0c;充电桩和汽车之间只能通过CP信号进行通讯&#xff0c;判断、控制充电电流和状态。 汽车充电桩CP信号…

QT之自定义表格控件

继承QWidget来绘制的一款自定义控件&#xff0c;设计原因是因为Qt自带的QTableWidget的大批量操作很卡&#xff0c;特别是在嵌入式设备上时。 该控件特色功能&#xff1a; 1、支持拖动自适应。 2、支持各种颜色&#xff0c;字体&#xff0c;行列数设置。 代码如下&#xff1a; …

win10安装cuda11.4及cudnn

查看nvidia驱动版本 在windows终端键入nvidia-smi&#xff0c;查看nvidia显卡驱动。显卡驱动的版本决定了CUDA的版本下限。 如果出现上述的问题&#xff0c;则终端进入C:\Program Files\NVIDIA Corporation\NVSMI文件夹内&#xff0c;再键入nvidia-smi&#xff0c;可以看到我…

[MySQL]MySQL视图特性

[MySQL]MySQL视图特性 文章目录 [MySQL]MySQL视图特性1. 视图的概念2. 视图的基本操作创建视图删除视图 3. 视图规则和限制 1. 视图的概念 视图是一个虚拟表&#xff0c;其内容由查询定义&#xff0c;同真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。基表是对…

Git标签管理(对版本打标签,起别名)

tag 理解标签创建标签git tag [name]git show [tagname] 操作标签删除标签git tag -d < tagname > 推送某个标签到远程git push origin < tagname > 理解标签 标签 tag &#xff0c;可以简单的理解为是对某次 commit 的⼀个标识&#xff0c;相当于起了⼀个别名。 …

数据库备份mysqldump、mydumper、xtrabackup

数据库备份&#xff0c;数据库为school&#xff0c;素材如下 1.创建student和score表 CREATE TABLE student ( id INT(10) NOT NULL UNIQUE PRIMARY KEY , name VARCHAR(20) NOT NULL , sex VARCHAR(4) , birth YEAR, department VARCHAR(20) , address VARCHAR(50) );创建sco…

《面试1v1》如何提高远程用户的吞吐量

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

FPGA配置文件从串并模式下载

FPGA配置文件的下载模式有5种&#xff1a; 主串模式&#xff08;master serial&#xff09;从串模式&#xff08;slave serial&#xff09;主并模式&#xff08;master selectMAP&#xff09;从并模式&#xff08;slave selectMAP&#xff09;JTAG模式 其中&#xff0c;JTAG模…

SQL 常见函数整理 _ SOUNDEX() 和 DIFFERENCE()

SOUNDEX() 1. 用法 是一种针对字符串进行音似匹配的函数。它将一个字符串作为输入&#xff0c;并返回一个代表该字符串音似编码的字符串。此编码可用于比较和匹配具有相似发音的字符串。 2. 基本语法 SOUNDEX(expression)其中&#xff0c;expression 是要进行音似编码的字符串…

后端排序优化——谁调用,谁排序

前言 为了使排序更加灵活&#xff0c;建议后端排序可以优化为“谁调用谁排序”。 代码实现 数据库设计 以学生表为例。 前端查询条件为姓名&#xff0c;住址。 Controller 简化后的controller层代码如下&#xff1a; /*** queryStudent[查询学生]* param name 姓名* param …

SQL优化——update优化

1.update优化 执行update语句的时候一定要根据索引字段进行更新&#xff0c;否则就会出现行锁升级为表锁的情况&#xff0c;锁住整张表&#xff0c;一旦锁表了并发性能就会受影响。 2.总结 sql优化事实上都是对索引进行优化