Codeforces663E Binary Table(FWT)

时间:2021-11-07 15:10:56

题目

Source

http://codeforces.com/contest/663/problem/E

Description

You are given a table consisting of n rows and m columns. Each cell of the table contains either 0 or 1. In one move, you are allowed to pick any row or any column and invert all values, that is, replace 0 by 1 and vice versa.

What is the minimum number of cells with value 1 you can get after applying some number of operations?

Input

The first line of the input contains two integers n and m (1 ≤ n ≤ 20, 1 ≤ m ≤ 100 000) — the number of rows and the number of columns, respectively.

Then n lines follows with the descriptions of the rows. Each line has length m and contains only digits '0' and '1'.

Output

Output a single integer — the minimum possible number of ones you can get after applying some sequence of operations.

Sample Input

3 4
0110
1010
0111

Sample Output

2

分析

题目大概说有一个n*m的01矩阵,每次可以选择将矩阵一整行或者一整列反转,要使最终矩阵里的1数量最少,问最少是多少。

由于n最大20,容易想到暴力做法(POJ3279),枚举各行是否反转的状态,然后遍历每一列累加各列能得到的最少1的个数。
然后就没有然后了。。

这题的解法这篇博客写得挺清楚的:http://taosama.github.io/2016/09/21/Codeforces%20662C%20C.%20Binary%20Table%EF%BC%88FWT%EF%BC%89/

  • $f[msk]=\sum_{k \in [0,\ 2^n) }cnt_k\times min(Ones_{msk\oplus k},\ n-Ones_{msk\oplus k})\ \ (cnt_k表示状态为k的列的个数)$

得出那个卷积,用FWT去搞,时间复杂度$O(2^nlog2^n)$,即$O(n2^n)$。

感觉这题挺有意思的。。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF (1<<30)
#define MAXN (1<<20) void FWT(long long *a,int n){
for(int d=1; d<n; d<<=1){
for(int m=d<<1,i=0; i<n; i+=m){
for(int j=0; j<d; ++j){
long long x=a[i+j],y=a[i+j+d];
a[i+j]=x+y; a[i+j+d]=x-y;
}
}
}
}
void UFWT(long long *a,int n){
for(int d=1; d<n; d<<=1){
for(int m=d<<1,i=0; i<n; i+=m){
for(int j=0; j<d; ++j){
long long x=a[i+j],y=a[i+j+d];
a[i+j]=(x+y)/2; a[i+j+d]=(x-y)/2;
}
}
}
}
void Convolution(long long *a,long long *b,int n){
FWT(a,n); FWT(b,n);
for(int i=0; i<n; ++i) a[i]=a[i]*b[i];
UFWT(a,n);
} int a[20][100000];
long long A[MAXN],B[MAXN]; int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0; i<n; ++i){
for(int j=0; j<m; ++j){
scanf("%1d",&a[i][j]);
}
}
for(int j=0; j<m; ++j){
int s=0;
for(int i=0; i<n; ++i){
s<<=1;
s|=a[i][j];
}
++A[s];
}
for(int i=0; i<(1<<n); ++i){
int cnt=0;
for(int j=0; j<n; ++j){
if(i>>j&1) ++cnt;
}
B[i]=min(cnt,n-cnt);
}
Convolution(A,B,1<<n);
long long res=INF;
for(int i=0; i<(1<<n); ++i){
res=min(res,A[i]);
}
printf("%I64d",res);
return 0;
}