CodeForces - 662C Binary Table (FWT)

題意:給一個N*M的0-1矩陣,能夠進行若干次操做,每次操做將一行或一列的0和1反轉,求最後能獲得的最少的1的個數.
分析:本題可用FWT求解.
由於其0-1反轉的特殊性且\(N\leq20\),將每一列j視做一個N位二進制數\(A[j]\),則一共有M個N位數,則能夠統計出每一個二進制數i的個數\(num[i]\).將全部的行反轉操做組合也視做一個N位二進制數\(S\).
那麼如何將本題與FWT結合? 首先根據異或運算的結合律:\(S\oplus A[j]=B\),則\(S = A[j] \oplus B\),\(B\)確定也是一個N位二進制數.處理出每一個二進制數對應最少的1的個數(由於咱們能夠將某一列也反轉)\(B[j]\),則對於每一種行操做的組合\(S\),最後獲得最少的1的個數即 \(cnt(S) = \sum_{i\oplus j = S}(num[i]*B[j])\).用FWT計算出每一個操做組合\(S\)的最少1個數,最後掃一遍全部操做,求其最小值便可.c++

#include<bits/stdc++.h>
using namespace std;
const int MAXN = (1<<20)+5;
typedef long long LL;

void FWT(LL 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++){
                LL x = a[i+j],y = a[i+j+d];
                a[i+j] = (x+y),a[i+j+d] = (x-y);
            }
        }
    }
}

void UFWT(LL 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++){
                LL x = a[i+j],y = a[i+j+d];
                a[i+j] = (x+y)/2; a[i+j+d] = (x-y)/2;
            }
        }
    }
}
void solve(LL a[],LL 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);
}

LL A[MAXN],B[MAXN],num[MAXN];
char str[MAXN];

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int N,M;
    scanf("%d %d",&N, &M);
    for(int i=0;i<N;++i){
        scanf("%s",str);
        for(int j=0;j<M;++j){
            A[j] |= ((str[j]-'0')<<i);
        }
    }
    for(int i=0;i<M;++i) num[A[i]]++;
    for(int i=0;i<(1<<N);++i){
        int cnt = __builtin_popcount(i);
        B[i] = min(cnt,N-cnt);
    }
    int all = 1<<N;
    solve(num,B,all);
    LL ans = N*M;
    for(int i=0;i<all;++i){
        ans = min(ans,num[i]);
    }
    printf("%lld\n",ans);
    return 0;
}
相關文章
相關標籤/搜索