1 第三题
1.1 题目描述
给定一个迷宫,找到最快从起点到达重点的路径所需要的步数。 假设迷宫如下,假定左上角坐标为(0, 0),右下角坐标为(3, 2)
1 0 -1 1
-2 0 -1 -3
2 2 0 0
-2是迷宫的起点,坐标为(0, 1)
-3是迷宫的终点,坐标为(3, 1)
-1代表障碍物,不能行走
1和2代表传送门,传送门门由正整数标示,只会成对出现。站在传送门上,能仅用一步就传送到相同数字的另一个传送i门的位置: 1只能传送到1, 2只能传送到2。站在传送门上也可以选择不传送。
从起点到终点有若干种走法,举例如下:
(0,1)->(1,1)->(1,2)->(2,2)->(3,2)->(3, 1),共花费5步
或者
(0,1)->(0, 0) -传送> (3,0)->(3,1),共花费3步 经检验3步是所需的最少步数,最后结果返回3。
1.2 输入描述
每一行输入都是用空格隔开的整数
第一行给出迷宫地图的长和宽,均为正整数
之后每一行的每一个数字,都代表迷宫的一格
-2表示起点,-3表示终点,-1表示不可通过的障碍物,0表示可通过的道路,大于0的正整数代表传送门,并且保证成对出现,在传送门上,可以仅用一步传送到另一一个相同数字的传送门]的位置。
1.3 输出描述
输出最少要多少步能够从起点走到终点。
输出-1如果没有任何办法从起点走到终点。
1.4 测试用例
1.4.1 input
4 3
1 0 -1 1
-2 0 -1 -3
2 2 0 0
1.4.2 output
3
1.5 代码示例
def find_shortest_path(m):
r = len(m) # 行数
c = len(m[0]) # 列数 迷宫第一行的长度
# 将start和end的初始值设为None
start = None
end = None
# 遍历迷宫的每个位置,找到起点和终点的坐标
for i in range(r):
for j in range(c):
if m[i][j] == -2:
start = (i, j)
elif m[i][j] == -3:
end = (i, j)
# 检查起点和终点是否成功找到,如果没有找到,则返回-1
if not start or not end:
return -1
# 定义四个方向的移动,右移,左移,下移,上移
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
queue = [(start, 0)] # 使用队列进行广度优先搜索 队列
visited = {start} # 记录已经访问过的位置 集合
while queue: # 队列不为空,执行循环
current, steps = queue.pop(0) # 取出队列中的第一个元素(current, steps),current表示当前格子坐标,steps表示到达当前格子所用的步数
if current == end: # 如果当前格子坐标等于终点坐标end,说明已经找到了最短路径,直接返回steps
return steps
for direction in directions: # 当前格子不是终点,则对当前格子的四个相邻格子进行遍历
next_pos = (current[0] + direction[0], current[1] + direction[1]) # 当前位置的横坐标+direction的横坐标,当前位置的纵坐标+direction的纵坐标
if 0 <= next_pos[0] < r and 0 <= next_pos[1] < c: # 判断next_pos是否在迷宫范围内
if m[next_pos[0]][next_pos[1]] != -1 and next_pos not in visited: # 判断next_pos是否可以通过(障碍物)及是否已经被访问过
queue.append((next_pos, steps + 1)) # 添加到队列,更新steps
visited.add(next_pos) # 添加到已访问集合中
return -1
# 读取迷宫地图
rows, cols = map(int, input("请输入迷宫地图的长和宽,以空格分隔:").split()) # 输入行数 列数,空格分隔,将分割后的字符串转换为整数,然后赋值给rows和cols.
maze = [] # 空列表,用于存储迷宫地图
for _ in range(rows):
row = list(map(int, input("请输入迷宫每行的格子值,以空格分隔:").split())) # 逐行读取迷宫地图
maze.append(row) # 将每一行添加到maze列表中
# 计算最少步数
shortest_steps = find_shortest_path(maze)
# 输出结果
print("最少要走的步数:", shortest_steps)
1.5.1 input
请输入迷宫地图的长和宽,以空格分隔:3 4
请输入迷宫每行的格子值,以空格分隔:1 0 -1 1
请输入迷宫每行的格子值,以空格分隔:-2 0 -1 -3
请输入迷宫每行的格子值,以空格分隔:2 2 0 0
1.5.2 output
最少要走的步数: 5
2 代码解释
2.1 current, steps = queue.pop(0)
这行代码从队列 queue
中取出第一个元素,并将其分解为 current
和 steps
两个变量。队列是一种先进先出的数据结构,通过 pop(0)
操作可以取出队列中的第一个元素。在这里,current
表示当前所在的位置,steps
表示从起点到达当前位置所经过的步数。这样,我们可以在后续的操作中使用 current
和 steps
来进行路径搜索和步数计算。
2.2 queue.append((next_pos, steps + 1))
queue.append((next_pos, steps + 1))
visited.add(next_pos)
在这段代码中,我们将下一个位置 next_pos
和当前步数 steps + 1
组成一个元组 (next_pos, steps + 1)
,并将其添加到队列 queue
中。这表示我们将继续以下一个位置为起点,步数加1的方式进行搜索。
同时,我们将下一个位置 next_pos
添加到集合 visited
中,表示该位置已经被访问过。这样可以避免重复访问同一个位置,以提高搜索效率。
通过以上操作,我们实现了广度优先搜索算法中的扩展操作,将下一个可行的位置加入队列,并标记为已访问。这样可以保证在搜索过程中不会遗漏任何可行的路径,并且不会陷入无限循环。
append
和 add
是不同的方法,用于在不同的数据结构中添加元素。
-
append
是列表(List)对象的方法,用于在列表末尾添加一个元素。在上述代码中,我们使用queue.append((next_pos, steps + 1))
将一个元组添加到队列的末尾。 -
add
是集合(Set)对象的方法,用于向集合中添加一个元素。在上述代码中,我们使用visited.add(next_pos)
将下一个位置添加到已访问的集合中。
两者的区别在于,append
是针对列表的操作,将元素添加到列表的末尾;而 add
是针对集合的操作,将元素添加到集合中。