小O的矩阵变换

[题目链接](https://www.nowcoder.com/practice/b1c40c55c4724f75a659915c6700e5ce)

思路

给定两个 的 01 矩阵 ,每次操作可以选择 的某一行或某一列,将该行/列所有元素翻转(0 变 1,1 变 0)。求最少操作次数使 变为 ,若不可能输出 -1。

建模

表示第 行是否被翻转, 表示第 列是否被翻转(翻转偶数次等价于不翻转,所以每行/列只需考虑翻转 0 或 1 次)。

对于每个位置 ,翻转后的值为 ,要求等于 ,即:

$$

,问题变为:能否找到 ,使得对所有 满足

枚举固定

整个方程组由 个未知数()组成。只要确定 的值(0 或 1),其余变量均可唯一确定:

  • 由第 0 行约束:,确定所有
  • 由第 列第 0 列约束:,确定所有

确定所有变量后,遍历全矩阵验证 是否对所有位置成立。若成立,操作次数为

分别尝试,取两者中满足条件的最小操作数;若两者均不满足,输出 -1。

样例演示

第一组():

$$

验证:,全部通过。

操作次数

同理得操作次数也为 2。答案为 2。

复杂度

  • 时间复杂度:,每组数据枚举两个 值,每次 验证。
  • 空间复杂度:

代码

[sol-C++]

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T--) {
        int n;
        cin >> n;

        vector<vector<int>> A(n, vector<int>(n)), B(n, vector<int>(n));
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                cin >> A[i][j];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                cin >> B[i][j];

        vector<int> r(n), c(n);
        int ans = INT_MAX;

        for (int r0 = 0; r0 <= 1; r0++) {
            r[0] = r0;
            for (int j = 0; j < n; j++)
                c[j] = (A[0][j] ^ B[0][j]) ^ r[0];
            for (int i = 1; i < n; i++)
                r[i] = (A[i][0] ^ B[i][0]) ^ c[0];

            bool ok = true;
            for (int i = 0; i < n && ok; i++)
                for (int j = 0; j < n && ok; j++)
                    if ((r[i] ^ c[j]) != (A[i][j] ^ B[i][j]))
                        ok = false;

            if (ok) {
                int ops = 0;
                for (int i = 0; i < n; i++) ops += r[i];
                for (int j = 0; j < n; j++) ops += c[j];
                ans = min(ans, ops);
            }
        }

        cout << (ans == INT_MAX ? -1 : ans) << "\n";
    }

    return 0;
}

[sol-Java]

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(br.readLine().trim());
        StringBuilder sb = new StringBuilder();

        while (T-- > 0) {
            int n = Integer.parseInt(br.readLine().trim());
            int[][] A = new int[n][n];
            int[][] B = new int[n][n];

            for (int i = 0; i < n; i++) {
                StringTokenizer st = new StringTokenizer(br.readLine());
                for (int j = 0; j < n; j++)
                    A[i][j] = Integer.parseInt(st.nextToken());
            }
            for (int i = 0; i < n; i++) {
                StringTokenizer st = new StringTokenizer(br.readLine());
                for (int j = 0; j < n; j++)
                    B[i][j] = Integer.parseInt(st.nextToken());
            }

            int[] r = new int[n];
            int[] c = new int[n];
            int ans = Integer.MAX_VALUE;

            for (int r0 = 0; r0 <= 1; r0++) {
                r[0] = r0;
                for (int j = 0; j < n; j++)
                    c[j] = (A[0][j] ^ B[0][j]) ^ r[0];
                for (int i = 1; i < n; i++)
                    r[i] = (A[i][0] ^ B[i][0]) ^ c[0];

                boolean ok = true;
                outer:
                for (int i = 0; i < n; i++)
                    for (int j = 0; j < n; j++)
                        if ((r[i] ^ c[j]) != (A[i][j] ^ B[i][j])) {
                            ok = false;
                            break outer;
                        }

                if (ok) {
                    int ops = 0;
                    for (int i = 0; i < n; i++) ops += r[i];
                    for (int j = 0; j < n; j++) ops += c[j];
                    ans = Math.min(ans, ops);
                }
            }

            sb.append(ans == Integer.MAX_VALUE ? -1 : ans).append('\n');
        }

        System.out.print(sb);
    }
}