有一个n行n列的棋盘,每个格子上都有一个硬币,且n为偶数。每个硬币要么是正面朝上,要么是反面朝上。每次操作你可以选定一个格子(x,y),然后将第x行和第y列的所有硬币都翻面。求将所有硬币都变成同一个面最少需要的操作数。
标签:fine closed img style tput iostream 题解 rip zoj
【样例说明】
对(2,3)和(3,1)进行操作,最后全变成1。
【数据规模】
对于100%的数据,n ≤ 1,000。
上来一看,第一反应,异或数学题,想了半天如何异或也没想出来,问呵呵酵母菌,他说他觉得是图论WTF?!图论有几个O(n)算法能在这道题用上的。
于是乎看了一眼题解:解异或方程组……
一个点最多翻一遍,这话不用再说了吧……
让我们先从都翻为0开始说起
我们设x[i][j]为第i,j个点是否要翻,a[i][j]为该点初始状态,则x[1][j]^x[2][j]^……^x[n][j]^x[i][1]^x[i][2]^x[i][m]^x[i][j]=a[i][j]。
我们把第i行和第j列所有的点按照上式列出方程组并合并, 由于n为偶数,则可以化为:
x[i][j]=a[1][j]^a[2][j]^……^a[n][j]^a[i][1]^a[i][2]^……^a[i][m]^a[i][j]。
那么我们只要对于每一行,每一列n^2预处理出他们的异或和再相加就好了。
至于都为1吗?由于n是偶数,我们只要把每一个点是否翻的状态取反就是答案。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <queue> 6 #include <algorithm> 7 #include <cmath> 8 #include <map> 9 #define N 1005 10 using namespace std; 11 int n,a[N][N]; 12 char b[N]; 13 int sum[2][N]; 14 int main() 15 { 16 scanf("%d",&n); 17 for(int i=1;i<=n;i++) 18 { 19 scanf("%s",b+1); 20 for(int j=1;j<=n;j++) 21 { 22 a[i][j]=b[j]-‘0‘; 23 } 24 } 25 for(int i=1;i<=n;i++) 26 { 27 for(int j=1;j<=n;j++) 28 { 29 sum[0][i]^=a[i][j]; 30 sum[1][j]^=a[i][j]; 31 } 32 } 33 int ans=0; 34 for(int i=1;i<=n;i++) 35 { 36 for(int j=1;j<=n;j++) 37 { 38 int t=sum[0][i]^sum[1][j]; 39 t^=a[i][j]; 40 ans+=t; 41 } 42 } 43 ans=min(ans,n*n-ans); 44 printf("%d\n",ans); 45 return 0; 46 }
标签:fine closed img style tput iostream 题解 rip zoj
原文地址:http://www.cnblogs.com/liutianrui/p/7674354.html