You are given a square binary grid, represented as a 2D list grid
of size n x n
. Each cell contains either a 0
or a 1
. You are allowed to swap any two adjacent rows (i.e., swap row i
and row i+1
as one move). Your goal is to arrange the grid so that for every row i
(0-indexed), all cells in columns right of i
(that is, columns i+1
to n-1
) are 0
. In other words, for each row, all the 1
s must be to the left of the diagonal or on the diagonal. You must return the minimum number of swaps needed to achieve this arrangement, or -1
if it is impossible.
-1
if no valid arrangement is possible.
At first glance, you might consider trying all possible row arrangements, but this would be extremely inefficient for larger grids. Instead, let's focus on what it means for a row to be "valid" at a given position. For row i
, all cells to the right of column i
must be 0
. In other words, the last n - i - 1
elements of row i
must be 0
. So, for each row, we can count the number of trailing zeros, and for each position i
, we need a row with at least n - i - 1
trailing zeros.
Instead of brute-forcing all permutations, we can:
Let's break down the algorithm step by step:
trailing_zeros
.i
(from top to bottom), determine the minimum number of trailing zeros required: required = n - i - 1
.i
that has at least required
trailing zeros.-1
.j
, swap it up to position i
(requires j - i
swaps).trailing_zeros
list to reflect the new row order.-1
.This greedy approach works because, for each row, we only need to ensure the constraint is satisfied at that position, and moving a row up does not break the constraints for previous rows.
Example Input:
grid = [
[0,0,1],
[1,1,0],
[1,0,0]
]
trailing_zeros = [0,1,2]
Output: 3
Each step moves the appropriate row up, and the process is complete when all constraints are satisfied.
O(n!)
time, which is infeasible for even moderate n
.O(n^2)
(since each row is length n
).O(n^2)
in total (since for each of n
rows, we may scan up to n
rows below).O(n^2)
.O(n)
for the trailing_zeros
list.
The key insight is to reduce the problem to counting trailing zeros in each row, and then greedily moving rows with enough trailing zeros up to their required positions using adjacent swaps. This avoids brute-force permutations and ensures an efficient O(n^2)
solution. The approach is both simple and effective, leveraging a clear understanding of the grid's constraints.
class Solution:
def minSwaps(self, grid):
n = len(grid)
trailing_zeros = []
for row in grid:
cnt = 0
for x in reversed(row):
if x == 0:
cnt += 1
else:
break
trailing_zeros.append(cnt)
swaps = 0
for i in range(n):
required = n - i - 1
j = i
while j < n and trailing_zeros[j] < required:
j += 1
if j == n:
return -1
while j > i:
trailing_zeros[j], trailing_zeros[j-1] = trailing_zeros[j-1], trailing_zeros[j]
swaps += 1
j -= 1
return swaps
class Solution {
public:
int minSwaps(vector<vector<int>>& grid) {
int n = grid.size();
vector<int> trailing_zeros(n, 0);
for(int i = 0; i < n; ++i) {
int cnt = 0;
for(int j = n - 1; j >= 0; --j) {
if(grid[i][j] == 0)
cnt++;
else
break;
}
trailing_zeros[i] = cnt;
}
int swaps = 0;
for(int i = 0; i < n; ++i) {
int required = n - i - 1;
int j = i;
while(j < n && trailing_zeros[j] < required)
++j;
if(j == n)
return -1;
while(j > i) {
swap(trailing_zeros[j], trailing_zeros[j-1]);
swaps++;
j--;
}
}
return swaps;
}
};
class Solution {
public int minSwaps(int[][] grid) {
int n = grid.length;
int[] trailingZeros = new int[n];
for (int i = 0; i < n; ++i) {
int cnt = 0;
for (int j = n - 1; j >= 0; --j) {
if (grid[i][j] == 0)
cnt++;
else
break;
}
trailingZeros[i] = cnt;
}
int swaps = 0;
for (int i = 0; i < n; ++i) {
int required = n - i - 1;
int j = i;
while (j < n && trailingZeros[j] < required)
j++;
if (j == n)
return -1;
while (j > i) {
int tmp = trailingZeros[j];
trailingZeros[j] = trailingZeros[j - 1];
trailingZeros[j - 1] = tmp;
swaps++;
j--;
}
}
return swaps;
}
}
var minSwaps = function(grid) {
const n = grid.length;
let trailingZeros = [];
for (let i = 0; i < n; ++i) {
let cnt = 0;
for (let j = n - 1; j >= 0; --j) {
if (grid[i][j] === 0)
cnt++;
else
break;
}
trailingZeros.push(cnt);
}
let swaps = 0;
for (let i = 0; i < n; ++i) {
let required = n - i - 1;
let j = i;
while (j < n && trailingZeros[j] < required)
j++;
if (j === n)
return -1;
while (j > i) {
[trailingZeros[j], trailingZeros[j-1]] = [trailingZeros[j-1], trailingZeros[j]];
swaps++;
j--;
}
}
return swaps;
};