BZOJ3517 翻硬币 异或方程

本文探讨了解决一个特定的编程问题:在一个01矩阵中,通过选择性地翻转行和列,使所有元素统一为同一数值的最少操作次数。文章详细解释了如何利用异或运算和矩阵特性来优化计算过程,最终达到O(n^2)的时间复杂度。

题目链接

题意:
给你一个 n ∗ n n*n nn的01矩阵,每次你可以选择一个 ( x , y ) (x,y) (x,y),作用是把 x x x这一行和 y y y这一列都进行01取反,问最少操作多少次可以使所有数字都全变成同一种。保证 n n n是偶数, n &lt; = 1000 n&lt;=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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值