题意:给定一 的 01 矩阵,每次可以翻转一行或一列,执行若干次操作。若将操作完成后的矩阵的每一行从做到右视为一个二进制数 ,每一列从上到下视为 ,要求 ,问是否可以实现,可以实现求最小操作次数。。
解法:假设第一行有一个 ,则 ,此时 ,即第一列每个数字都是 ,则此时 ,则要求整个矩阵都是 。因而全 矩阵是合理的。
如果第一行全 ,则 ,则 ,整个矩阵全 。
因而整个矩阵必须所有数字都相同。
考虑维护方程 和 。如果最后是全 ,如果当前 ,则 ,反之相等。因而维护一个 01 种类并查集即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 2000;
char a[N + 5][N + 5];
// [1, n] row0; [n+1,2n] row1
// [2*n+1,3*n] col0
int father[4 * N + 5], n;
int getfather(int x)
{
return father[x] == x ? x : father[x] = getfather(father[x]);
}
int getid(int id, int flag, int col)
{
int ans = id;
if (flag)
ans += 2 * n;
if (col)
ans += n;
return ans;
}
void merge(int x, int y)
{
x = getfather(x);
y = getfather(y);
if (x != y)
father[x] = y;
}
bool check(int x, int y)
{
return getfather(x) == getfather(y);
}
int solve(int op)
{
int m = n * 2;
for (int i = 1; i <= 2 * m; i++)
father[i] = i;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (a[i][j] == ('1' ^ op))
{
merge(getid(i, 0, 0), getid(j, 1, 1));
merge(getid(i, 0, 1), getid(j, 1, 0));
}
else
{
merge(getid(i, 0, 0), getid(j, 1, 0));
merge(getid(i, 0, 1), getid(j, 1, 1));
}
for (int i = 1; i <= n; i++)
if (check(i, i + n))
return -1;
for (int i = 1; i <= n; i++)
if (check(i + 2 * n, i + 3 * n))
return -1;
map<int, int> siz;
for (int i = 1; i <= n; i++)
siz[getfather(i)]++;
for (int i = 2 * n + 1; i <= 3 * n; i++)
siz[getfather(i)]++;
int ans = 2 * n;
for (auto [x, y] : siz)
ans = min(ans, y);
return min(ans, 2 * n - ans);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%s", a[i] + 1);
if (solve(0) == -1)
{
printf("-1");
return 0;
}
printf("%d", min(solve(0), solve(1)));
return 0;
}