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;
}

 

 

posted @ 2020-01-28 21:08  xjqxjq  阅读(103)  评论(0编辑  收藏  举报