本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
题目链接: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
解释:交换 0 和 5 ,1 步完成
输入: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种思路:- 把二维数组变成字串(以作为唯一标记)时,要先把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
四、总结
常常遇到需要访问四周或者往四周的其中一个方向行走,且对于每一个方向处理思路都相同的情况,其实都可以把四周的偏差存入数组,再进行遍历。这样写可以少很多冗余的代码。 类似的需要访问四周的情景还有很多,比如下棋、表格行走等。
如果你觉的还不错的话,给我点个赞吧💐