LeetCode 773. 滑动谜题|刷题打卡

258 阅读3分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

题目链接:LeetCode 773. 滑动谜题
难度:困难

一、题目描述

在一个 2 x 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示.
一次移动定义为选择 0 与一个相邻的数字(上下左右)进行交换.
最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。
给出一个谜板的初始状态,返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。
示例:

输入:board = [[1,2,3],[4,0,5]]
输出:1
解释:交换 051 步完成
输入:board = [[1,2,3],[5,4,0]]
输出:-1
解释:没有办法完成谜板
输入:board = [[4,1,2],[5,0,3]]
输出:5
解释:
最少完成谜板的最少移动次数是 5 ,
一种移动路径:
尚未移动: [[4,1,2],[5,0,3]]
移动 1 次: [[4,1,2],[0,5,3]]
移动 2 次: [[0,1,2],[4,5,3]]
移动 3 次: [[1,0,2],[4,5,3]]
移动 4 次: [[1,2,0],[4,5,3]]
移动 5 次: [[1,2,3],[4,5,0]]
输入:board = [[3,2,4],[1,5,0]]
输出:14

提示:

  • board 是一个如上所述的 2 x 3 的数组.
  • board[i][j] 是一个 [0, 1, 2, 3, 4, 5] 的排列.

二、思路分析

先看1步棋能走到哪,再基于上一步棋,第2步棋能走到哪,最终走到目标点时,返回走了几步就好,当然也有可能走完了走不到,最后返回-1即可。思路还是BFS(其实就是暴力穷举),难倒是不难,但就是很复杂,有几个要注意的点:

  • 如何表达『已访问』?
    把当前状态转换成唯一的key,存入set中,即可方便的查重。转换成唯一key有2种思路:
    1. 把二维数组变成字串(以作为唯一标记)时,要先把2维变为一维,再将数字变为字符串。
    2. 将二维数组转换为一维数组,再转为tuple,这样作为唯一标识更高效。
  • 如何访问『四周』?
    上下左右的坐标相当于当前位置偏差分别是[0,1],[0,-1],[1,0],[-1,0],可以作为一个数组,再遍历这个数组。在每轮遍历中,当前位置加上偏差得到新的位置,再:判断是否越界,深拷贝一个board,并交换新旧位置的0,得到新board。
  • 复制board时要用深拷贝
  • 因为过程很复杂,尽量抽成函数,让思路清晰一点。

三、AC 代码

Python

def slidingPuzzle(self, board: List[List[int]]) -> int:
        def getState(board):
            return tuple(board[0] + board[1])

        def getPositionOfZero(board):
             for i in range(3):
                for j in range(3):
                    if curBoard[i][j] == 0:
                        return (i,j)

        def getAjdBoards(board, row, col):
            arr = [ [0,1],[0,-1],[1,0],[-1,0] ]
            boards = []
            for x, y in arr:
                newRow = row+x
                newCol = col+y
                if newRow < 0 or newRow >= 2 or newCol < 0 or newCol >= 3:
                    continue
                newBoard = copy.deepcopy(board)
                newBoard[row][col],newBoard[newRow][newCol] = newBoard[newRow][newCol],newBoard[row][col]
                boards.append(newBoard)
            # print(board, "got", boards)
            return boards

        targetBoard = [[1,2,3],[4,5,0]]
        visited = set([getState(board)])
        
        queue = deque([(board,0)])
        while len(queue) != 0:
            curBoard, moves = queue.popleft()
            if curBoard == targetBoard:
                return moves
            row,col = getPositionOfZero(curBoard)
            boards = getAjdBoards(curBoard, row, col)
            for b in boards:
                state = getState(b)
                if state not in visited:
                    visited.add(state)
                    queue.append((b,moves+1))

        return -1

四、总结

常常遇到需要访问四周或者往四周的其中一个方向行走,且对于每一个方向处理思路都相同的情况,其实都可以把四周的偏差存入数组,再进行遍历。这样写可以少很多冗余的代码。 类似的需要访问四周的情景还有很多,比如下棋、表格行走等。

如果你觉的还不错的话,给我点个赞吧💐