题目大意:n*n的01矩阵,每次操作选择(x,y),将第x行和第y列取反,求
将01矩阵全部变为相同数字的最少操作数
题解:由于取反就是异或1,根据异或运算的自反性,每个点最多只能翻一次,脑补(后发现,变成全0+全1=n*n。假定全变成0。对于每个点,翻与不翻设为
x[i][j]
,考虑(i,j),对它有影响的是纵坐标为j的x(u,j)和横坐标为i的x(i,v),根据它最后为0可以得到一个方程:
x[1][j]
^
x[2][j]
^
...
^
x[n][j]
^
x[i][1]
^
x[i][2]
^
...
^
x[i][n]
^
a[i][j]
^
x[i][j]=0
把常量移到后面,得到
x[1][j]
^
x[2][j]
^
...
^
x[n][j]
^
x[i][1]
^
x[i][2]
^
...
^
x[i][n]
^
x[i][j]=
a[i][j]
有n^2个变量和n^2个方程,所以有唯一解
然后可以搞一搞异或方程组。高斯消元?
找(i,j)和(i,xx)和(xx,j)异或,由于n为偶数,左边只剩下了
x[i][j]
,右边是所有和
ij
有关系的
a[i][j]
的异或和……
我的收获:异或强啊,菜啊
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int M=1005;
int n,ans;
int a[M][M],sumx[M],sumy[M];
char s[M];
void work()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans+=sumx[i]^sumy[j]^a[i][j];
printf("%d\n",min(ans,n*n-ans));
}
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=n;j++)
a[i][j]=s[j]-'0',sumx[i]^=a[i][j],sumy[j]^=a[i][j];
}
}
int main()
{
init();
work();
return 0;
}