您的位置:首页 > 其它

BZOJ - 3517 翻硬币(异或)

2016-12-03 03:50 155 查看
题目:

Description

有一个n行n列的棋盘,每个格子上都有一个硬币,且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。

分析:(以下6个数组都是0-1数组,S也是0或1)

假设输入矩阵是num[i][j],第i行的异或和是row[i],第j行的异或和是col[j](0<=i<n,0<=j<n)

要求的矩阵是xnum[i][j],第i行的异或和是xrow[i],第j行的异或和是xcol[j],n*n个xnum[i][j]的异或和为S

因为不知道最后是全部变成1还是全部变成0,所以有2种情况

第一种情况:

那么根据题意,num[i][j]^xrow[i]^xcol[j]^xnum[i][j]=0   ------(1)

将(1)式中的j取遍0至n-1,再将n个式子求异或,根据异或的交换性和结合性从而化简得到:

row[i]^(xrow[i]*n%2)^S^xrow[i]=0

因为n是偶数所以row[i]^S^xrow[i]=0   ------(2)

同理col[j]^S^xcol[j]=0   ------(3)

(1)(2)(3)式的异或和为num[i][j]^row[i]^col[j]^xnum[i][j]=0

即xnum[i][j]=num[i][j]^row[i]^col[j]

第二种情况:

与第一种情况同理,xnum[i][j]=num[i][j]^row[i]^col[j]^1

第一种情况和第二种情况刚好完全互补,其中1较少的那种就是结果

无论是第一种还是第二种,由num计算xnum的方法和由xnum计算num的方法是一样的,这是一种高度的对称性,这一点,和点亮所有的灯十分相似

代码:

#include <iostream>
using namespace std;

int num[1001][1001], row[1001], col[1001];

int main()
{
int n, sum = 0;
cin >> n;
char c;
for (int i = 0; i < n; i++)row[i] = col[i] = 0;
for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)
{
cin >> c;
num[i][j] = c - '0';
row[i] += num[i][j], col[j] += num[i][j];
}
for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)
sum += (row[i] + col[j] + num[i][j]) % 2;
cout << ((sum * 2>n*n) ? n*n - sum : sum) << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: