cf 663E Binary Table
题目描述
有 $n$ 行 $m$ 列的 $01$ 网格,可以选择翻转某些行或某些列,求最终 $1$ 最少的数量。
数据范围
$n \le 20,m \le 10^5$
题解
假设状态 $x$ 是行的翻转状态,那每一列的状态 $y$ 会变成 $z=x \wedge y$ ,再根据 $0,1$ 数量的大小比较决定这一列是否翻转
变个式子, $x=y \wedge z$
于是我们可以设 $Y_i$ 为状态为 $i$ 的列数, $Z_i$ 为 $i$ 中 $min(0,1)$ 的个数,利用 $fwt$ 进行卷积,可以得到最小的 $(Y \wedge Z)[x]$ ,这个值就是答案
效率: $O(n2^n)$
代码
#include <bits/stdc++.h> typedef long long LL; using namespace std; const int N=1<<20; int n,m,a[N],t,b[N],p=1e9; LL A[N],B[N]; char s[N]; void Fwt(LL *g,int o){ for (int i=1;i<t;i<<=1) for (int j=0;j<t;j+=(i<<1)) for (int k=0;k<i;k++){ LL x=g[j+k],y=g[i+j+k]; g[j+k]=x+y;g[i+j+k]=x-y; if (!o) g[j+k]>>=1,g[i+j+k]>>=1; } } int main(){ cin>>n>>m;t=1<<n; for (int i=0;i<n;i++){ scanf("%s",s); for (int j=0;j<m;j++) a[j]=(a[j]<<1)|(s[j]^48); } for (int i=0;i<m;i++) A[a[i]]++; for (int i=0;i<t;i++) b[i]=b[i>>1]+(i&1), B[i]=min(b[i],n-b[i]); Fwt(A,1);Fwt(B,1); for (int i=0;i<t;i++) A[i]=A[i]*B[i]; Fwt(A,0); for (int i=0;i<t;i++) p=min(p,(int)A[i]); cout<<p<<endl;return 0; }