http://codeforces.com/contest/662/problem/C
题意:
n行m列01矩阵,每次可以反转一行或一列,问最后最少可以剩下多少个1
n只有20,把行状态压缩
操作奇数次相当于1次,操作偶数次相当于不操作
所以可以枚举对行的操作,将操作也状态压缩
A[i] 表示有多少列的状态为i
B[i] 表示 状态为i时的最优解,即fanzh
C[i] 表示 操作i的最优解
执行一次行操作相当于给某一列的状态异或上操作的状态
C[opt] = Σ A[state]*B[opt xor state]
(先执行这种行操作,如果某一列发现再执行一次列操作更优,那这一列再执行列操作,这通通包含在B数组里)
目前求C的复杂度为 2^2n
令 res = opt xor state
C[opt] = Σ(state) Σ(res) [state xor res == opt] A[state]*B[res]
C[opt] = Σ(state) Σ(res) [state xor opt == res] A[state]*B[res]
然后就可以用FWT 优化 成2^n * n
这还有个用子集反演的,是什么啊??
http://blog.csdn.net/QWsin/article/details/55054071
#include<cstdio> #include<algorithm> using namespace std; typedef long long LL; char s[21][100002]; LL a[1048578],b[1048578],c[1048576]; int count(int i) { int sum=0; while(i) sum+=i&1,i>>=1; return sum; } void FWT_xor(LL *a,int n) { LL x,y; 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) { x=a[i+j]; y=a[i+j+d]; a[i+j]=x+y; a[i+j+d]=x-y; } } void IFWT_xor(LL *a,int n) { LL x,y; 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) { x=a[i+j]; y=a[i+j+d]; a[i+j]=x+y>>1; a[i+j+d]=x-y>>1; } } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%s",s[i]+1); int state; for(int i=1;i<=m;++i) { state=0; for(int j=1;j<=n;++j) if(s[j][i]=='1') state|=1<<j-1; ++a[state]; } int S=1<<n,sum; for(int i=0;i<S;++i) { sum=count(i); b[i]=min(sum,n-sum); } FWT_xor(a,S); FWT_xor(b,S); for(int i=0;i<S;++i) c[i]=a[i]*b[i]; IFWT_xor(c,S); LL ans=n*m; for(int i=0;i<S;++i) ans=min(ans,c[i]); printf("%I64d",ans); }