1. 程式人生 > >Bzoj3517 翻硬幣題解

Bzoj3517 翻硬幣題解

%d output gre space 包含 [1] printf 呵呵 con

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

Bzoj3517 翻硬幣題解