排布二进制网格的最少交换次数
难度:
标签:
题目描述
给你一个 n x n
的二进制网格 grid
,每一次操作中,你可以选择网格的 相邻两行 进行交换。
一个符合要求的网格需要满足主对角线以上的格子全部都是 0 。
请你返回使网格满足要求的最少操作次数,如果无法使网格符合要求,请你返回 -1 。
主对角线指的是从 (1, 1)
到 (n, n)
的这些格子。
示例 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 解释:所有行都是一样的,交换相邻行无法使网格符合要求。
示例 3:
输入:grid = [[1,0,0],[1,1,0],[1,1,1]] 输出:0
提示:
n == grid.length
n == grid[i].length
1 <= n <= 200
grid[i][j]
要么是0
要么是1
。
代码结果
运行时间: 43 ms, 内存: 17.5 MB
/* 思路:
1. 使用流操作找到每行最右侧的1的位置。
2. 逐行检查是否满足条件,不满足则寻找下一行并交换。
3. 统计交换次数。
*/
import java.util.stream.IntStream;
public class Solution {
public int minSwaps(int[][] grid) {
int n = grid.length;
int[] maxRightOnes = IntStream.range(0, n)
.map(i -> findRightmostOne(grid[i]))
.toArray();
int swaps = 0;
for (int i = 0; i < n; i++) {
if (maxRightOnes[i] < i) {
int j = IntStream.range(i, n)
.filter(k -> maxRightOnes[k] >= i)
.findFirst().orElse(-1);
if (j == -1) return -1;
for (int k = j; k > i; k--) {
swap(maxRightOnes, k, k - 1);
swaps++;
}
}
}
return swaps;
}
private int findRightmostOne(int[] row) {
return IntStream.range(0, row.length)
.map(i -> row[row.length - 1 - i] == 1 ? row.length - 1 - i : -1)
.filter(i -> i != -1)
.findFirst().orElse(-1);
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
解释
方法:
此题解首先对每一行计算从最后一个元素开始到第一个1的位置,存储在数组pos中。然后,遍历这个pos数组,尝试将每个行的最后一个1移动到对角线以下的合适位置。如果某行的最后一个1的位置超过了其行号,则需要通过交换行来调整位置。对于每一个目标行i,从i开始向下查找可以移动到该行的1的最大位置的行,记录交换次数,然后进行实际的行交换。如果找不到可以交换的行,返回-1。否则,返回总的交换次数。
时间复杂度:
O(n^2)
空间复杂度:
O(n)
代码细节讲解
🦆
在题解中,你提到了计算每一行最右边的1的位置并存储在数组pos中。这种方法是为了解决什么样的问题?
▷🦆
对于pos数组的填充过程,为何选择从每行的最后一个元素向前查找,而不是从前向后?
▷🦆
题解中提到,如果某行的最后一个1的位置超过了其行号,则需要交换行。能否具体解释为什么超过行号的1会导致需要交换行?
▷🦆
在查找可以移动到行i的行的过程中,为何选择一旦找到就停止查找,这种策略会不会影响到最终的交换次数?
▷