某變換好題。不過據說還有O(2^n*n^2)DP的……數組
給定一個n*m的01矩陣,你能夠選擇對任意行和任意列取反,使得最終「1」的數量儘可能少。spa
第一行兩個整數n,m。
接下來n行,每行m個字符,描述一個01矩陣。code
一個整數表示最少的1的數量。blog
3 4
0110
1010
0111ip
2get
1 <= n <= 20,1 <= m <= 100000。string
首先發現矩陣只有20行,通過一番腦補,能夠把這二十行壓成一個數。it
而後咱們就獲得了m個數。io
而後在行上的取反就至關於將這m個數同時異或上同一個數。ast
而後咱們要求的就是,找出一個數,使得這m個數同時異或上這個數後,每一個數的二進制位中的0和1的個數的最小值總和最小。
咱們設ans[x]爲當異或的數爲x時的答案,a數組用來存放m個數,d[x]爲x的二進制位中0和1的個數的最小值。
因此:
,
咱們稍微改一改,用w[x]表示在m個數中,爲x的數有多少個:
。
等等,是否是發現了什麼?這不就是卷積FWT的式子嗎?
。
時間複雜度O(nm+2^n*n)。
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define MS 23 #define MN 100005 #define MM 1100005 using namespace std; char c[MS][MN]; int a[MN]; ll A[MM],B[MM],C[MM]; int n,m,ans; inline int read() { int n=0,f=1; char c=getchar(); while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();} while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();} return n*f; } void FWT(ll* a,int len,bool g) { register int wt,st,i; ll x,y; for (wt=1;wt<len;wt<<=1) for (st=0;st<len;st+=wt<<1) for (i=0;i<wt;++i) { x=a[st+i]; y=a[st+wt+i]; a[st+i]=x+y; a[st+wt+i]=x-y; if (g) a[st+i]>>=1,a[st+wt+i]>>=1; } } int main() { register int i,j; n=read(); m=read(); ans=n*m; for (i=0;i<n;++i) scanf("%s",c[i]+1); for (i=n-1;i>=0;--i) for (j=1;j<=m;++j) a[j]=(a[j]<<1)+c[i][j]-'0'; for (i=1;i<=m;++i) ++A[a[i]]; for (i=0;i<(1<<n);++i) B[i]=B[i>>1]+(i&1); for (i=0;i<(1<<n);++i) B[i]=min(B[i],n-B[i]); FWT(A,1<<n,false); FWT(B,1<<n,false); for (i=0;i<(1<<n);++i) C[i]=A[i]*B[i]; FWT(C,1<<n,true); for (i=0;i<(1<<n);++i) ans=min(ans,(int)C[i]); printf("%d",ans); }
若是把FWT中的if語句改爲(x+y)/g,(x-y)/g,效率會慢5倍,除法真是個可怕的東西。