Bzoj3517 翻硬币题解 解异或方程组

3517: 翻硬币

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 281  Solved: 211
[Submit][Status][Discuss]

Description

有一个nn列的棋盘,每个格子上都有一个硬币,且n为偶数。每个硬币要么是正面朝上,要么是反面朝上。每次操作你可以选定一个格子(x,y),然后将第x行和第y列的所有硬币都翻面。求将所有硬币都变成同一个面最少需要的操作数。

Input

第一行包含一个正整数n
接下来n行,每行包含一个长度为n的01字符串,表示棋盘上硬币的状态。

Output

仅包含一行,为最少需要的操作数。

Sample Input

4
0101
1000
0010
0101

Sample Output

2

HINT

 

【样例说明】

对(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 }
View Code

 

posted @ 2017-10-15 21:32  Hzoi_joker  阅读(548)  评论(2编辑  收藏  举报