少儿Python每日一题(24):迷宫问题

原题解答

本次的题目如下所示(题目来源:蓝桥杯):

编程实现:

两名宇航员在探索一个未知行星,行星上有一些障碍物,这些障碍物用数字1表示,没有障碍物用数字0表示。行星被表示成一个N*M的矩阵。探索过程中两名宇航员走散了。已知A宇航员的位置(x1,y1)和B宇航员的位置(x2,y2),请你帮助A宇航员找到一条最短路径到达B宇航员的位置,并输出最短路径的长度(不包括起点)。

注意:

1.x1、x2表示矩阵的行号,y1、y2表示矩阵的列号;

2.左上角的位置为(0,0);

3.A、B宇航员的位置只能在数字0上;

4.有障碍物的位置不能通过。

例如:当N=4,M=5,x1=1,y1=0,x2=3,y2=3,A宇航员的位置(1,0),B宇航员的位置(3,3),矩阵表示如下:

 

其中,从A到B的最短路线用箭头表示,最短路线长度为7。

输入描述

第一行输入两个正整数N,M,正整数之间以空格隔开

接下来的N行,每行输入M个数,数字1表示障碍物,数字0表示没有障碍物,数字之间以空格隔开

第N+2行输入四个整数,分别表示x1,y1,x2,y2,整数之间以空格隔开

输出描述

输出一个数,表示A宇航员到达B宇航员位置的最短路径的长度(不包括起点)

样例输入

4 5

0 0 0 0 0

0 1 0 1 0

0 1 0 0 0

0 0 1 0 0

1 0 3 3

样例输出

7

本道题是典型的广度优先搜索算法(BFS)问题。BFS算法的思想是从根节点开始,沿着树的宽度遍历树的节点。如果所有的节点都被访问,则算法中止。它的主要思想如下:

  1. 1. 将根节点放入队列中
  2. 2. 从队列中取出第一个节点,把检验它是否为目标。如果找到目标,则结束搜索并返回结果;否则将它尚未监测的直接子节点放入队列中
  3. 3. 如果队列为空,表示整张图都检查过了没有找到目标

本题中所有的节点都在一个二维矩阵中。因此,它可能的子节点分别为上、下、左、右四个方向。我们从(x1, y1)的位置开始,寻找其子节点,子节点再按照这个步骤找下一层的子节点,直到找完整张图为止,则(x2, y2)的坐标步数即为我们需要的答案。

本题的代码行数较多,我们对代码进行分段讲解:

n, m = map(int, input().split()) # 输入n和m的值,即矩阵的行数和列数

maze = []
for _ in range(n):
    maze.append(list(map(int, input().split())))
# 输入迷宫的矩阵

x1, y1, x2, y2 = map(int, input().split())
# 输入两名宇航员位置的坐标

 以上代码按照题目的要求完成相关的输入。

steps = [[-1] * m for _ in range(n)] 
# 生成行为n、列为m的矩阵,所有元素初始值为-1(-1代表不可达,用自然数代表到达该坐标的步数)

dx = [0, 0, 1, -1] # 用于位置的移动,dx为x坐标变化,前面两个数为0
dy = [1, -1, 0, 0] # 用于位置的移动,dy为y坐标变化,后面两个数为0

que = [] # 用于存储队列

steps[x1][y1] = 0
que.append((x1, y1))

以上代码定义了4个变量,steps用于存储步数,用于存储到达每一个点需要的最短步数,初始值都设置为-1,后面每遍历到对应的坐标,将值改为步数值。dx和dy用于上下左右的遍历。que用于存储队列。本题从A的位置出发,因此到达A需要0步,首先将A所在位置的步数设置为0,并将A的坐标放入队列中。

while len(que) != 0:
    x, y = que.pop(0) # 队列的第一个元素出列
    for i in range(4): # 遍历上下左右四个方向
        nx = x + dx[i] 
        ny = y + dy[i]
        if nx < 0 or nx >= n or ny < 0 or ny >= m: 
            continue # 超出矩阵的范围,跳过
        if maze[nx][ny] == 1:
            continue # 遇到障碍物,跳过
        if steps[nx][ny] != -1:
            continue # 该位置已经被遍历果,跳过
        que.append((nx, ny)) # 将该位置放入队列
        steps[nx][ny] = steps[x][y] + 1 # 步数值为上一步的步数+1

以上代码为最为核心的部分,在队列不为空的情况下,我们一直寻找下去。在循环中,首先将队列的第一个元素出列,并遍历上下左右四个方向是否有路可走。

首先,我们要排除超出矩阵行列范围的坐标。

其次,我们要判断这条路是否通。如果该坐标在迷宫中的值为1,则说明此坐标为障碍物,不可以走。

然后,我们要判断这条路是否已经被遍历过,如果该坐标的值为-1则表示没有被遍历过,否则已经被遍历过。

除了以上的三种情况外,该坐标在可行走的路线中,加入队列中。步数值为上一步的步数+1。

当整个矩阵都遍历完成后,循环结束。

print(steps[x2][y2])

直接打印出坐标(x2, y2)在steps中的值即可得到题目的答案。

本题拓展

本题考查了广度优先搜索算法,题目难度:★★★★★★

本题的难度在于用到了数据结构中关于树和图论的相关知识,而本题会出现在蓝桥杯青少省赛的中级组压轴题中(最低参赛年龄为小学五年级)。此阶段系统学习数据结构是不现实的,但是涉及到迷宫问题时,此类题型经常会有涉及。

做这类题型时,记住以下步骤即可:

每次从队列中取出第一个元素,搜索附近所有可用的节点,并放入队列中,直到搜索完所有的节点。

上面的题目中仅要求输出最短路径的长度,如果对题目进行扩展,要求输出最短路径经过的坐标。题目可以变成如下所示:

编程实现:

 两名宇航员在探索一个未知行星,行星上有一些障碍物,这些障碍物用数字1表示,没有障碍物用数字0表示。行星被表示成一个N*M的矩阵。探索过程中两名宇航员走散了。已知A宇航员的位置(x1,y1)和B宇航员的位置(x2,y2),请你帮助A宇航员找到一条最短路径到达B宇航员的位置,并输出最短路径的长度(不包括起点)。

注意:

1.x1、x2表示矩阵的行号,y1、y2表示矩阵的列号;

2.左上角的位置为(0,0);

3.A、B宇航员的位置只能在数字0上;

4.有障碍物的位置不能通过。

例如:当N=4,M=5,x1=1,y1=0,x2=3,y2=3,A宇航员的位置(1,0),B宇航员的位置(3,3),矩阵表示如下:

其中,从A到B的最短路线用箭头表示,最短路线(1, 0) (0,0) (0, 1) (0, 2) (1, 2) (2, 2) (3, 2) (3, 3)。

输入描述

第一行输入两个正整数N,M,正整数之间以空格隔开

接下来的N行,每行输入M个数,数字1表示障碍物,数字0表示没有障碍物,数字之间以空格隔开

第N+2行输入四个整数,分别表示x1,y1,x2,y2,整数之间以空格隔开

输出描述

输出宇航员A到达宇航员B位置所经过的所有点的坐标,以空格隔开

样例输入

4 5

0 0 0 0 0

0 1 0 1 0

0 1 0 0 0

0 0 1 0 0

1 0 3 3

样例输出

(1, 0) (0,0) (0, 1) (0, 2) (1, 2) (2, 2) (3, 2) (3, 3)

该题目中需要记录经过的所有路径的坐标。这里我们大体的思路不变,需要变化的是记录下每次经过的点的坐标。我们可以使用字典类型记录,字典的键是当前点的坐标,值为上一个点的坐标。同时使用一个集合记录已经访问过的点的坐标。

当到达终点位置时,再从字典中取出相应的值。由于顺序是从后往前,还需要进行倒序排列。

具体代码如下:

def path(maze, x1, y1, x2, y2):
    if not maze or not maze[0]:
        return -1, []

    m, n = len(maze), len(maze[0])

    # 记录已经访问过的位置和最短路径
    visited = set()
    visited.add((x1, y1))
    p = {(x1, y1): None}

    # 定义四个方向,上下左右
    dx = [0, 0, 1, -1]
    dy = [1, -1, 0, 0]

    # 使用队列来进行广度优先搜索
    queue = [(x1, y1, 0)]

    while queue:
        i, j, steps = queue.pop(0)
        if i == x2 and j == y2:
            result = []
            while (i, j) != (x1, y1):
                result.append((i, j))
                i, j = p[(i, j)]
            result.append((x1, y1))
            result.reverse()
            return result

        for k in range(4):
            ni, nj = i + dx[k], j + dy[k]
            if 0 <= ni < m and 0 <= nj < n and maze[ni][nj] == 0 and (ni, nj) not in visited:
                visited.add((ni, nj))
                queue.append((ni, nj, steps+1))
                p[(ni, nj)] = (i, j)
    return []


n, m = map(int, input().split())  # 输入n和m的值,即矩阵的行数和列数

maze = []
for _ in range(n):
    maze.append(list(map(int, input().split())))
# 输入迷宫的矩阵

x1, y1, x2, y2 = map(int, input().split())
# 输入两名宇航员位置的坐标
p = path(maze, x1, y1, x2, y2)
print(*p)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凤城老人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值