1536. 排布二进制网格的最少交换次数
大约 4 分钟
--- 

1536. 排布二进制网格的最少交换次数
- 标签:贪心、数组、矩阵
- 难度:中等
题目链接
题目大意
描述:给定一个 的二进制矩阵 。每次操作可以交换相邻两行。
要求:返回使得矩阵满足"主对角线右上方的所有元素都是 "所需的最少交换次数。如果无法做到,返回 。
说明:主对角线右上方指 对于所有 。即每行从右往左数的连续 的个数有一定要求。
示例:
- 示例 1:

输入:grid = [[0,0,1],[1,1,0],[1,0,0]]
输出:3- 示例 2:

输入:grid = [[0,1,1,0],[0,1,1,0],[0,1,1,0],[0,1,1,0]]
输出:-1
解释:所有行都是一样的,交换相邻行无法使网格符合要求。解题思路
思路 1:贪心
1. 核心思想
对于第 行(从上到下,-based),要求该行从右侧开始至少有 个连续的 (即 到行尾都是 )。
换句话说,第 行要求最后 个元素全为 ,第 行要求最后 个元素全为 ,...,第 行要求最后 个元素为 ,第 行没有要求。
先计算每行从右往左的连续 的个数 。然后对于每行 (从 到 ),找一个满足 的最靠上的行 (),将 行交换到 行,交换次数为 。
2. 具体步骤
第 1 步:计算每行的后缀 个数 。
第 2 步:创建一个标记数组 。
第 3 步:从 到 :
- 找最小的 使得 且 。
- 如果找不到,返回 。
- 将 移到 :(此处的交换需要通过将 与上方的行依次交换,但我们可以直接计算交换次数)。
- 标记 。
第 4 步:累计交换次数。
3. 交换次数的计算
如果选择将第 行交换到第 行(),需要 次交换(逐行向上交换)。但需要注意,后面的选择会影响前面的结果。
更简单的实现:每次找到需要的行 后,从 开始向上冒泡交换到 ,同时更新 数组。
ans = 0
for i in range(n):
if right_zeros[i] >= n - i - 1:
continue
j = i + 1
while j < n and right_zeros[j] < n - i - 1:
j += 1
if j == n:
return -1
# 将 j 行向上交换到 i
for k in range(j, i, -1):
right_zeros[k], right_zeros[k-1] = right_zeros[k-1], right_zeros[k]
ans += 14. 举例说明
以 为例:
0 0 1 → 从右连续 0 数: 0
1 1 0 → 从右连续 0 数: 1
1 0 0 → 从右连续 0 数: 2要求:第 行需要 个后缀 ,第 行需要 个。
- :需要 。第 行只有 个,找 ,,,。将第 行换到第 行,交换次数 。
- :需要 。现在第 行(原第 行),找 (原第 行)。交换次数 。
- :需要 。自动满足。
总交换次数 。
思路 1:代码
class Solution:
def minSwaps(self, grid: List[List[int]]) -> int:
n = len(grid)
# 计算每行从右开始的连续 0 个数
zeros = [0] * n
for i in range(n):
cnt = 0
for j in range(n - 1, -1, -1):
if grid[i][j] == 0:
cnt += 1
else:
break
zeros[i] = cnt
ans = 0
for i in range(n):
need = n - i - 1
if zeros[i] >= need:
continue
# 找下面满足条件的行
j = i + 1
while j < n and zeros[j] < need:
j += 1
if j == n:
return -1
# 将第 j 行向上交换到第 i 行
for k in range(j, i, -1):
zeros[k], zeros[k - 1] = zeros[k - 1], zeros[k]
ans += 1
return ans思路 1:复杂度分析
- 时间复杂度:,,可行。
- 空间复杂度:。