$n \leq 20,m \leq 100000$的01矩阵,可整行整列01翻转,问最少剩几个1.
一个暴力的做法是枚举$2^n$种行翻转然后$m$列扫一遍。但其实在行翻转情况确定的情况下我们只关心两个东西:某一列在行翻转后剩几个1,以及有几个这样的列。$f(i,j)$--在行翻转$j$的情况下,有$i$个1的有多少列。其实就是与$j$有$i$个位不同的有多少列。可以枚举每一个位置$p$,那么这一位上与$j$不同的状态$f(i-1,j \ \ xor \ \ 2^p)$可以加过来,但要挑去其中$p$已经算过一次的情况,有$f(i-2,j)$这么多种,又要从$f(i-2,j)$中挑去那些$p$这一位算过一次的情况,$f(i-3,j \ \ xor \ \ 2^p)$,如此循环。但这样枚举完每个位置之后,每种好的情况其实算了$i$次,所以$i \times f(i,j)=\sum_{p=0}^{n-1} \sum_{t=1}^{i}(-1)^{t-1}f(i-t,j \ \ xor \ \ (2^p \times (t \mod 2)))$。
这样是$2^nn^3$的,但可以发现$\sum_{p=0}^{n-1}\sum_{t=3}^{i}(-1)^{t-1}f(i-t,j \ \ xor \ \ (2^p \times (t \mod 2)))=(i-2) \times f(i-2,j)$,所以整理一下,$i \times f(i,j)=\sum_{p=0}^{n-1}f(i-1,j \ \ xor \ \ 2^p)+(i-2-n)f(i-2,j)$。少一个$n$。
要再少一个得用FWT。不会。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 //#include<math.h> 5 //#include<set> 6 //#include<queue> 7 //#include<bitset> 8 //#include<vector> 9 #include<algorithm> 10 #include<stdlib.h> 11 using namespace std; 12 13 #define LL long long 14 int qread() 15 { 16 char c; int s=0,f=1; while ((c=getchar())<‘0‘ || c>‘9‘) (c==‘-‘) && (f=-1); 17 do s=s*10+c-‘0‘; while ((c=getchar())>=‘0‘ && c<=‘9‘); return s*f; 18 } 19 20 //Pay attention to ‘-‘ , LL and double of qread!!!! 21 22 int K,n; 23 #define maxn 1100011 24 int b[maxn],f[22][maxn]; 25 26 int main() 27 { 28 K=qread(); n=qread(); 29 { 30 char c; 31 for (int i=0;i<K;i++) 32 for (int j=1;j<=n;j++) 33 { 34 while ((c=getchar())!=‘0‘ && c!=‘1‘); 35 b[j]|=(c-‘0‘)<<i; 36 } 37 for (int i=1;i<=n;i++) f[0][b[i]]++; 38 } 39 int T=1<<K,ans=0x3f3f3f3f; 40 for (int i=1;i<=K;i++) 41 { 42 for (int j=0;j<T;j++) 43 { 44 if (i>1) f[i][j]=(i-2-K)*f[i-2][j]; 45 for (int k=0;k<K;k++) f[i][j]+=f[i-1][j^(1<<k)]; 46 f[i][j]/=i; 47 } 48 } 49 for (int i=0;i<T;i++) 50 { 51 int tmp=0; 52 for (int j=0;j<=K;j++) tmp+=min(j,K-j)*f[j][i]; 53 ans=min(ans,tmp); 54 } 55 printf("%d\n",ans); 56 return 0; 57 }
原文地址:https://www.cnblogs.com/Blue233333/p/9206929.html