1536.排布二进制网格的最少交换次数

目标

给你一个 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 。

提示:

  • For each row of the grid calculate the most right 1 in the grid in the array maxRight.
  • To check if there exist answer, sort maxRight and check if maxRight[i] ≤ i for all possible i's.
  • If there exist an answer, simulate the swaps.

思路

有一个 n x n 的二进制矩阵,每次操作可以交换相邻的两行,求使得矩阵主对角线 之上 的所有格子变为 0 所需的最小操作次数。

i 行最右侧的 1 的下标不能超过 i,如果不满足条件,找到第一个满足条件的行进行交换。这种贪心策略之所以可行,是因为如果存在多个满足条件的行,由于行从上到下的条件越来越宽松,满足当前行的条件也必定满足后续行,因此选最近的行交换即可。

代码


/**
 * @date 2026-03-02 8:45
 */
public class MinSwaps1536 {

    public int minSwaps(int[][] grid) {
        int n = grid.length;
        int[] maxRight = new int[n];
        for (int i = 0; i < n; i++) {
            for (int j = n - 1; j >= 0; j--) {
                if (grid[i][j] == 1) {
                    maxRight[i] = j;
                    break;
                }
            }
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (maxRight[i] <= i) {
                continue;
            }
            boolean flag = false;
            int prev = maxRight[i];
            for (int j = i + 1; j < n; j++) {
                if (maxRight[j] <= i) {
                    res += j - i;
                    flag = true;
                    maxRight[j] = prev;
                    break;
                }
                int tmp = maxRight[j];
                maxRight[j] = prev;
                prev = tmp;
            }
            if (!flag) {
                return -1;
            }
        }
        return res;
    }

}

性能