迷宫问题
题目如下:
描述
定义一个二维数组N*M(其中2<=N<=10;2<=M<=10),如5 × 5数组下所示:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。入口点为[0,0],既第一格是可以走的路。
本题含有多组数据。
输入描述:
输入两个整数,分别表示二维数组的行数,列数。再输入相应的数组,其中的1表示墙壁,0表示可以走的路。数据保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。
输出描述:
左上角到右下角的最短路径,格式如样例所示。
示例1
输入:
5 5
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0输出:
(0,0)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)
示例2
输入:
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 1
0 1 1 1 0
0 0 0 0 0输出:
(0,0)
(1,0)
(2,0)
(3,0)
(4,0)
(4,1)
(4,2)
(4,3)
(4,4)
说明:
注意:不能斜着走!!
python代码
while True:
try:
n, m = [int(x) for x in input().split()] #输入迷宫的大小
maze = []
for i in range(n): #输入迷宫
maze.append([int(x) for x in input().split()])
points = [] #记录走过的点的矩阵(初始化全为0)
for i in range(n):
points.append([])
for j in range(m):
points[i].append(0)
path = [] #记录路径
def get_points(i,j,maze,points): #寻找下一个可能的点(上下左右)
# print('test3',i,j)
k = []
if(0 <= i+1 < n and maze[i+1][j] == 0 and points[i+1][j] == 0):
k.append([i+1,j])
if(0 <= i-1 < n and maze[i-1][j] == 0 and points[i-1][j] == 0):
k.append([i-1,j])
if(0 <= j+1 < m and maze[i][j+1] == 0 and points[i][j+1] == 0):
k.append([i,j+1])
if(0 <= j-1 < m and maze[i][j-1] == 0 and points[i][j-1] == 0):
k.append([i,j-1])
# print('test4',k)
return k
def get_path(i,j,maze,path,points): #递归函数
global res #全局变量res,记录最终的最短路径的
k = get_points(i,j,maze,points) #先判断下一个可能的点
points[i][j] = 1 #记录当前的点已经走过了
# print('test2',k)
if(len(k) > 0): #如果存在下一个可能的点,就继续,否则说明路堵死了,开始回溯
for num1 in range(len(k)): #取出下一个可能的点
if(num1 == 0): #如果对当前点这是第一次走下一个点,则路径加上下一个点
path.append(k[num1])
else: #如果已经不是第一次了,则修改路径最后的点即可
path[-1] = k[num1]
if(k[num1] == [n - 1,m - 1]): #如果到了终点
if(len(res) == 0 or len(path) < len(res)): #判断是否是最短路径,是的话进行修改
res = path
else: #如果没到终点
tpath = path.copy()
tpoints = points.copy() #这里进行两个浅复制
# print('test1',tpath,tpoints)
get_path(k[num1][0],k[num1][1],maze,tpath,tpoints) #递归调用函数
global res #声明全局变量
res = []
get_path(0,0,maze,path,points)
#这是最后的输出
print('(0,0)')
for num2 in range(len(res)):
print('(%d,%d)'%(res[num2][0],res[num2][1]))
except:
break
解释
先声明思路是学“冲就完事”博主(牛客网)的题解的(“真正python 迷宫最短路径解法(华为机试)”),
把原帖贴上:真正python 迷宫最短路径解法(华为机试)
他的代码很详细,大家可以去琢磨一下他的思路。
很好的一道递归题,下面先对递归做个小总结。
首先递归就是函数自己调用自己,当然要有结束嵌套的情况,不能一直调用。函数调用的情况可以看作是压栈,最后调用的那一次先有结果或返回,然后返回倒数第二次,最后返回到最初的函数调用。
那么遵循以上原则,对此题思路做个解释。
首先是有迷宫的输入,此外我们对路劲和走过的点要做记录,这里设为path,points。
然后就是函数部分,我们有起点(0,0),这个作为最初的函数输入,进入函数后第一步先判断下一个可以走的点和把当前点记作走过的点,我们为了简洁递归里的代码段,再嵌套一个寻找下一个点的函数,返回的是下一个可能的点的集合k(以序列的形式),然后如果没有下一个可能的点了(也就是路堵死了),就结束函数,如果有下一个点的可能,则让路径添加该点,判断是否到达了终点。如果到达了,则判断路径长度是否最短,最短的话则把结果路径修改(res),否则不变。没有的话,对路径和走过的点进行一个浅复制,递归调用函数。
在函数返回的时候,如果在返回的点处还有另外可能的点,则修改路径的最后一项(也就是该点第一次选择的路)。可以这样修改的原因是浅复制,后续调用的时候不会改变当前路径的记录,详细想了解的可以去搜浅复制。
欢迎大家多来交流。