Codeforces663E Binary Table(FWT)
题目
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; }