【CF662C】Binary Table
题意:给你一个$n\times m$的01网格,你可以进行任意次操作,每次操作是将一行或一列的数都取反,问你最多可以得到多少个1?
$n\le 20,m\le 10^5$
题解:我也不知道叫啥了,说状压也不对,说fwt也不太对,就叫按位处理得了。
显然有$O(2^nm)$暴力,先枚举每行是否取反,然后枚举每列,如果0多就取反,否则不取。
但我们发现我们完全可以将本质相同的列一起处理,什么叫本质相同的列呢?假如我们对每行是否取反的状态为S,则所有$xor S$中1的个数相同的列我们都认为是相同的。那么现在问题就变成了对于所有S,$xor S$中1的个数为i的列的个数是多少。我们可以设f[S][i]表示这个状态,初始时f[T][0]++(T是某一列的状态)。然后我们枚举二进制的每一位是否取反,再从大到小枚举1的个数,进行转移即可。最后的答案就是$max\{\sum\limits_{i=0}^n f[S][i]\times max\{i,n-i\}\}$。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int n,m,ans; char s[21][100001]; int f[21][(1<<20)+1]; int main() { scanf("%d%d",&n,&m),ans=1<<30; int i,j,k; for(i=0;i<n;i++) scanf("%s",s[i]); for(i=0;i<m;i++) { int tmp=0; for(j=0;j<n;j++) if(s[j][i]==‘1‘) tmp|=1<<j; f[0][tmp]++; } for(i=0;i<n;i++) for(j=n;j;j--) for(k=0;k<(1<<n);k++) f[j][k]+=f[j-1][k^(1<<i)]; for(k=0;k<(1<<n);k++) { int tmp=0; for(i=0;i<=n;i++) tmp+=min(i,n-i)*f[i][k]; ans=min(ans,tmp); } printf("%d",ans); return 0; }