BZOJ3517 翻硬币 异或方程

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/84653854

题目链接

题意:
给你一个 n n n*n 的01矩阵,每次你可以选择一个 ( x , y ) (x,y) ,作用是把 x x 这一行和 y y 这一列都进行01取反,问最少操作多少次可以使所有数字都全变成同一种。保证 n n 是偶数, n < = 1000 n<=1000

题解:
我们先假设要全都变成0。我们发现对一个位置 ( x , y ) (x,y) 有影响的操作只会是所有操作中行是 x x 的和所有操作中列是 y y 的操作。我们发现把同一个位置翻转两次就会变回原来的状态,这个很像异或两次1答案不变,于是我们记 x [ i ] [ j ] x[i][j] 为这个位置是否变成和初始状态不同的一面,如果变的话 x [ i ] [ j ] = 1 x[i][j]=1 ,否则 x [ i ] [ j ] = 0 x[i][j]=0 ,我们设 a [ i ] [ j ] a[i][j] ( i , j ) (i,j) 这个位置在一开始是0还是1。我们可以对每个位置列出一个方程,一共列出 n 2 n^2 个异或方程,形式是 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 ] [ j ] x[i][j] ,原因是在前面的式子里被异或了两遍消掉了,然后还要异或上自己的初始值,如果要变成1就是等式右边等于1。稍微变化一下,等式两边同时异或 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]

对于这 n 2 n^2 个方程,我们暴力去解的话似乎是 n 6 n^6 的,所以我们需要优化解方程的过程。我们考虑我们要求的其实是每一个位置的 x [ i ] [ j ] x[i][j] 的和,于是我们想对于表示 ( i , j ) (i,j) 这个位置的式子,把其他含 x x 的式子都消掉,因为是未知的,取而代之我们要的是含 a a 的式子,因为 a a 是已知的。于是我们想办法消去那些其他的含 x x 的变量,我们去一个一个的异或,对于一个 x [ u ] [ j ] x[u][j] 或者 x [ i ] [ v ] x[i][v] ,每次去异或 ( u , j ) (u,j) 或者是 ( i , v ) (i,v) 那个位置的方程。这样我们会发现其实我们会把整个矩阵全都异或一遍,由于 n n 是个偶数,那么如果我们异或了一个横坐标不是 i i 并且纵坐标不是 j j x [ u ] [ v ] x[u][v] ,那么它所在的那一整行或者一整列就全部会被异或到,于是会被异或 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] 。我们发现,对于这个式子,我们只需要预处理出每一行和每一列的异或和,再异或上当前点的初始值,就可以知道这个点要变成0时的操作次数,然后对于所有的点求个和就好了。这样就是 O ( n 2 ) O(n^2) 的了。然而我们求出全变成0的答案之后并不用重新再求一遍全都变成1的答案,只需要有 n 2 n^2- 全变成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;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/84653854
今日推荐