题意:
给你一个
n
∗
n
n*n
n∗n的01矩阵,每次你可以选择一个
(
x
,
y
)
(x,y)
(x,y),作用是把
x
x
x这一行和
y
y
y这一列都进行01取反,问最少操作多少次可以使所有数字都全变成同一种。保证
n
n
n是偶数,
n
<
=
1000
n<=1000
n<=1000。
题解:
我们先假设要全都变成0。我们发现对一个位置
(
x
,
y
)
(x,y)
(x,y)有影响的操作只会是所有操作中行是
x
x
x的和所有操作中列是
y
y
y的操作。我们发现把同一个位置翻转两次就会变回原来的状态,这个很像异或两次1答案不变,于是我们记
x
[
i
]
[
j
]
x[i][j]
x[i][j]为这个位置是否变成和初始状态不同的一面,如果变的话
x
[
i
]
[
j
]
=
1
x[i][j]=1
x[i][j]=1,否则
x
[
i
]
[
j
]
=
0
x[i][j]=0
x[i][j]=0,我们设
a
[
i
]
[
j
]
a[i][j]
a[i][j]为
(
i
,
j
)
(i,j)
(i,j)这个位置在一开始是0还是1。我们可以对每个位置列出一个方程,一共列出
n
2
n^2
n2个异或方程,形式是
x
[
i
]
[
1
]
x
o
r
x
[
i
]
[
2
]
.
.
.
x
o
r
x
[
i
]
[
n
]
x
o
r
x
[
1
]
[
j
]
x
o
r
x
[
2
]
[
j
]
.
.
.
x
o
r
x
[
n
]
[
j
]
x
o
r
x
[
i
]
[
j
]
x
o
r
a
[
i
]
[
j
]
=
0
x[i][1]\ xor\ x[i][2]...\ xor\ x[i][n]\ xor\ x[1][j]\ xor\ x[2][j]...\ xor\ x[n][j]\ xor\ x[i][j]\ xor\ a[i][j]=0
x[i][1] xor x[i][2]... xor x[i][n] xor x[1][j] xor x[2][j]... xor x[n][j] xor x[i][j] xor a[i][j]=0,最后要异或上自己的
x
[
i
]
[
j
]
x[i][j]
x[i][j],原因是在前面的式子里被异或了两遍消掉了,然后还要异或上自己的初始值,如果要变成1就是等式右边等于1。稍微变化一下,等式两边同时异或
a
[
i
]
[
j
]
a[i][j]
a[i][j],变成
x
[
i
]
[
1
]
x
o
r
x
[
i
]
[
2
]
.
.
.
x
o
r
x
[
i
]
[
n
]
x
o
r
x
[
1
]
[
j
]
x
o
r
x
[
2
]
[
j
]
.
.
.
x
o
r
x
[
n
]
[
j
]
x
o
r
x
[
i
]
[
j
]
=
a
[
i
]
[
j
]
x[i][1]\ xor\ x[i][2]...\ xor\ x[i][n]\ xor\ x[1][j]\ xor\ x[2][j]...\ xor\ x[n][j]\ xor\ x[i][j]=a[i][j]
x[i][1] xor x[i][2]... xor x[i][n] xor x[1][j] xor x[2][j]... xor x[n][j] xor x[i][j]=a[i][j]。
对于这 n 2 n^2 n2个方程,我们暴力去解的话似乎是 n 6 n^6 n6的,所以我们需要优化解方程的过程。我们考虑我们要求的其实是每一个位置的 x [ i ] [ j ] x[i][j] x[i][j]的和,于是我们想对于表示 ( i , j ) (i,j) (i,j)这个位置的式子,把其他含 x x x的式子都消掉,因为是未知的,取而代之我们要的是含 a a a的式子,因为 a a a是已知的。于是我们想办法消去那些其他的含 x x x的变量,我们去一个一个的异或,对于一个 x [ u ] [ j ] x[u][j] x[u][j]或者 x [ i ] [ v ] x[i][v] x[i][v],每次去异或 ( u , j ) (u,j) (u,j)或者是 ( i , v ) (i,v) (i,v)那个位置的方程。这样我们会发现其实我们会把整个矩阵全都异或一遍,由于 n n n是个偶数,那么如果我们异或了一个横坐标不是 i i i并且纵坐标不是 j j j的 x [ u ] [ v ] x[u][v] x[u][v],那么它所在的那一整行或者一整列就全部会被异或到,于是会被异或 n n n次抵消掉,最后会变成 x [ i ] [ j ] = a [ i ] [ 1 ] x o r a [ i ] [ 2 ] . . . x o r a [ i ] [ n ] x o r a [ 1 ] [ j ] x o r a [ 2 ] [ j ] . . . x o r a [ n ] [ j ] x o r a [ i ] [ j ] x[i][j]=a[i][1]\ xor\ a[i][2]...\ xor\ a[i][n]\ xor\ a[1][j]\ xor\ a[2][j]...\ xor\ a[n][j]\ xor\ a[i][j] x[i][j]=a[i][1] xor a[i][2]... xor a[i][n] xor a[1][j] xor a[2][j]... xor a[n][j] xor a[i][j]。我们发现,对于这个式子,我们只需要预处理出每一行和每一列的异或和,再异或上当前点的初始值,就可以知道这个点要变成0时的操作次数,然后对于所有的点求个和就好了。这样就是 O ( n 2 ) O(n^2) O(n2)的了。然而我们求出全变成0的答案之后并不用重新再求一遍全都变成1的答案,只需要有 n 2 − n^2- n2−全变成0的答案就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,x[1010],y[1010],ans;
char s[1010][1010];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
if(s[i][j]=='1')
{
x[i]^=1;
y[j]^=1;
}
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
ans+=x[i]^y[j]^(s[i][j]-'0');
}
printf("%d\n",min(ans,n*n-ans));
return 0;
}