迷宫问题

148 阅读2分钟

题目

给定一个矩阵,元素的取值只有0和1。

我们用这样的一个矩阵表示迷宫:1代表墙壁,0代表可以人走的路。

左上角为入口,右下角为出口。

问题:根据输入的矩阵,寻找一条从入口到出口的路线。

例如:

输入

0, 1, 0, 0, 0,
0, 1, 1, 1, 1,
0, 1, 0, 1, 1,
0, 0, 0, 0, 1,
1, 0, 1, 0, 0,

对应的输出可能为(“#”表示经过的路线)

#, 1, 0, 0, 0, 

#, 1, 1, 1, 1, 

#, 1, 0, 1, 1, 

#, #, #, #, 1, 

1, 0, 1, #, #, 

分析

解题思路

深度优先搜索:优先往某一个方向不断深入搜索。比如先往左搜索,如果左侧还能继续搜索下去,那继续往左侧深入搜索,直到左侧不能再深入了,再换个方向,往换的方向继续深入搜索。

从起点开始,从上下左右四个方向深度优先搜索,探索可能的行进路线。如果走到了终点,表明已经找到从入口到出口的路线,则停止搜索。

解答

import unittest
import numpy as np


def print_maze(maze_map: np.array):
    """打印出行走路线"""
    shape = maze_map.shape
    print("一种迷宫走法:")
    for i in range(shape[0]):
        for j in range(shape[1]):
            if maze_map[i][j] == 8:
                print(f"#, ", end="")
            else:
                print(f"{maze_map[i][j]}, ", end="")
        print(f"\n")


def can_move(maze_map: np.array, x, y, direction) -> bool:
    """
    处在(x,y)位置,检查是否可以往direction所指示的方向行进
    :param maze_map:
    :param x: 行位置
    :param y: 列位置
    :param direction: 方向,分别使用1,2,3,4来表示上下左右。
    :return: 是否可以行进
    """
    shape = maze_map.shape
    
    # 这些条件判断,一定要小心核对,如果粗心写错,且意识不到哪里写错了,
    # 在递归调用的场景下,很难通过调试找到原因。
    # 可以往上走
    if direction == 1 and x > 0 and maze_map[x-1][y] == 0:
        return True
    # 可以往下走
    if direction == 2 and x+1 < shape[0] and maze_map[x+1][y] == 0:
        return True
    # 可以往左走
    if direction == 3 and y > 0 and maze_map[x][y-1] == 0:
        return True
    # 可以往右走
    if direction == 4 and y+1 < shape[1] and maze_map[x][y+1] == 0:
        return True
    # 其他情况,走不了
    return False


def move(maze_map: np.array, x, y, direction) -> (int, int):
    """处在(x,y)位置,往direction所指示的方向行进一步"""
    x1 = x
    y1 = y
    if direction == 1:
        x1 -= 1
    if direction == 2:
        x1 += 1
    if direction == 3:
        y1 -= 1
    if direction == 4:
        y1 += 1
    return x1, y1  # 返回新的位置(坐标)


def walk_maze(maze_map: np.array, x0, y0, x1, y1):
    maze_map[x0][y0] = 8
    for direction in range(1, 5): # 从上下左右四个方向进行探索
        if can_move(maze_map, x0, y0, direction):  # 此方向可以行进
            cur_x, cur_y = move(maze_map, x0, y0, direction)  # 往这个方向行进一步
            maze_map[cur_x][cur_y] = 8  # 标记该点已经走过
            if cur_x == x1 and cur_y == y1:
                print_maze(maze_map)
                # return  # 如果想探索各种走出迷宫的路线,则不要注释该句。
            walk_maze(maze_map, cur_x, cur_y, x1, y1)
            # 回溯,非常重要!探索其他方向时,对上一个方向探索的走过标记进行恢复。
            maze_map[cur_x][cur_y] = 0


# 以下为测试代码,验证程序的正确性
class Test(unittest.TestCase):
    def test_case1(self):
        maze_map = np.array(
            [
                [0, 1, 0, 0, 0],
                [0, 1, 1, 1, 1],
                [0, 1, 0, 1, 1],
                [0, 0, 0, 0, 1],
                [1, 0, 1, 0, 0],
            ],
            dtype=np.int32)
        walk_maze(maze_map, 0, 0, 4, 4)