📌  相关文章
📜  将所有 1 移动到给定方阵的单个单元格的最小步骤(1)

📅  最后修改于: 2023-12-03 15:09:36.232000             🧑  作者: Mango

将所有 1 移动到给定方阵的单个单元格的最小步骤

问题描述

给定一个 $n \times n$ 的方阵,每个单元格内填充了 0 或 1。需要找到一种最小步骤的方法,将所有的 1 移动到一个指定的单元格中。在每一步,可以执行以下操作之一:

  • 将位置 (i, j) 与位置 (i+1, j) 上下交换;
  • 将位置 (i, j) 与位置 (i, j+1) 左右交换。

要求输出最小的操作步数。

解决方法

此问题可以转换成最短路径问题。首先将方阵中的所有 1 看作节点,将其互相连接。因为只能做上下或左右交换,所以节点之间的距离为 1。将指定的单元格看作终点,从任意一个 1 开始做广度优先搜索,每到达一个 1 就将其设为起点,一直搜索到终点。这样,搜索到终点的时间就是所有节点到终点的最小步数之和。

代码实现
from collections import deque

def minStepsToMoveOnes(matrix, row, col, targetRow, targetCol):
    """
    :param matrix: List[List[int]]
    :param row: int
    :param col: int
    :param targetRow: int
    :param targetCol: int
    :return: int
    """
    dx, dy = [0, 1, 0, -1], [1, 0, -1, 0]
    start, end = row*col, targetRow*col+targetCol
    que = deque([(start, 0)])
    visited = set([start])
    node2Idx = {}
    cnt = 0

    # convert nodes to indices
    for i in range(row):
        for j in range(col):
            if matrix[i][j]:
                node2Idx[i*col+j] = cnt
                cnt += 1

    # convert problem to shortest path problem and run bfs
    while que:
        curr, distance = que.popleft()
        if curr == end:
            return distance
        currX, currY = curr // col, curr % col
        for i in range(4):
            nextX, nextY = currX+dx[i], currY+dy[i]
            if nextX >= 0 and nextX < row and nextY >= 0 and nextY < col:
                nextNode = nextX*col+nextY
                if matrix[nextX][nextY] and nextNode not in visited:
                    nextIdx = node2Idx[nextNode]
                    que.append((nextNode, distance+abs(nextIdx-cnt)))
                    visited.add(nextNode)

    return -1
复杂度分析

时间复杂度为 $O(n^2 \log n)$,空间复杂度为 $O(n^2)$,其中 $n$ 为方阵的边长。搜索中最多遍历 $n^2$ 个节点,每个节点都需要用 $O(\log n)$ 的时间计算其在公式中的值。空间复杂度分别为 visited、que 和 node2Idx 的空间和。其中 visited 和 que 的空间复杂度均为 $O(n^2)$,node2Idx 的空间复杂度均为 $O(\text{number_of_ones})$,其中 number_of_ones 为 1 的个数,一定不超过 $n^2$。