[BZOJ 3517]翻硬币

3517: 翻硬币

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 289  Solved: 219
[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。

 

 

Source

题解

第一眼看起来怕是个高斯消元解异或方程组...然而这样会搞出 $n^2$ 个方程, 消元过程又需要 $O(n^3)$ 的时间复杂度, 总时间复杂度会达到 $O(n^6)$ , 在 $n\leq 1000$ 的数据下不超时才是见鬼...

仔细观察方程特征:

如果我们设格子 $(a,b)$ 翻还是不翻为 $x_{a,b}$ , 初始状态设为 $d_{a,b}$ , 则可以得到形如下的方程:

\[\left( \bigoplus_{j=1}^n x_{a,j} \right)\oplus \left (\bigoplus_{i=1}^n x_{i,b}\right)\oplus d_{a,b}\oplus x_{a,b}=0\]

然后进行变形, 将 $d_{a,b}$ 移到左端, 得到:

\[\left( \bigoplus_{j=1}^n x_{a,j} \right)\oplus \left (\bigoplus_{i=1}^n x_{i,b}\right)\oplus x_{a,b}=d_{a,b}\]

然后我们发现这 $n^2$ 个方程中有大量的重复项, 再加上异或运算的特殊性, 我们将有重复项的相关方程互相异或抵消掉相同项, 最后剩下的情况大概是形如这个样子的:

\[x_{a,b}=\left( \bigoplus_{j=1}^n d_{a,j} \right)\oplus \left (\bigoplus_{i=1}^n d_{i,b}\right)\oplus d_{a,b}\]

这样可以优化到单次求值 $O(n)$ , 对于 $n^2$ 个未知数总时间复杂度为 $O(n^3)$ (然而依然跑不过)

但是我们可以发现都是某行某列的异或和, 读入时顺便求一下异或和存到某个数组就可以了.

最终总时间复杂度 $O(n^2)$

参考代码

GitHub

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <iostream>
 5 #include <algorithm>
 6 
 7 const int MAXN=1010;
 8 
 9 int n;
10 char buf[MAXN];
11 int xorSumx[MAXN];
12 int xorSumy[MAXN];
13 int data[MAXN][MAXN];
14 
15 int main(){
16     scanf("%d",&n);
17     for(int i=0;i<n;i++){
18         scanf("%s",buf);
19         for(int j=0;j<n;j++){
20             data[i][j]=buf[j]-'0';
21             // printf("%d\n",data[i][j]);
22             xorSumx[i]^=data[i][j];
23             xorSumy[j]^=data[i][j];
24             // printf("%d %d\n",xorSumx[i],xorSumy[j]);
25         }
26     }
27     int ans=0;
28     for(int i=0;i<n;i++){
29         for(int j=0;j<n;j++){
30             ans+=data[i][j]^xorSumx[i]^xorSumy[j];
31         }
32     }
33     printf("%d\n",std::min(n*n-ans,ans));
34     return 0;
35 }
Backup

 

 

posted @ 2017-10-17 21:00  rvalue  阅读(310)  评论(0编辑  收藏  举报