codeforces662C. Binary Table(快速沃尔什变换,动态规划)

传送门:http://codeforces.com/contest/662/problem/C


做法一:
f [ i ] [ j ] f[i][j] f[i][j] 表示对行选取 j j j 的状态,有 i i i 1 1 1 的列的数量
按照顺序枚举选取的行进行转移保证不重不漏
O ( 2 n n 2 ) O(2^nn^2) O(2nn2)
做法二:
f i f_i fi 表示选取行状态为 i i i 的方案
a i a_i ai 表示状态为 i i i 的列数
b i b_i bi 表示状态为 i i i 时这一列的答案
f i = ∑ j ⨁ k = i a j b k f_i=\sum_{j\bigoplus k =i} a_jb_k fi=jk=iajbk
写个 F W T FWT FWT 就好了

做法一代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
void gi(int &sum){
    sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(!flag) sum = -sum;
}
const int p = 998244353;
int n,m;
int f[21][1050000],a[21][101000];
char s[101000];
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
    gi(n); gi(m);
    rep(i,1,n){
    	scanf("%s",s+1);
    	rep(j,1,m) a[i][j] = s[j] == '1';
	}
	rep(j,1,m){
		int now = 0;
		rep(i,1,n) if(a[i][j]) now += (1<<(i-1));
		f[0][now]++;
    }
    rep(i,1,n) repp(j,i,1) rep(s,0,(1<<n)-1)
        f[j][s] += f[j-1][s^(1<<(i-1))];
    int ans = n*m+1,now;
	rep(s,0,(1<<n)-1){
		now = 0;
    	rep(i,1,n-1) now += f[i][s] * min(i,n-i);
    	ans = min(ans,now);
	}
	printf("%d\n",ans);
	return 0;
}

做法二代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
void gi(int &sum){
    sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(!flag) sum = -sum;
}
const int p = 998244353;
inline int ksm(int a,int x){int now = 1;for(;x;x>>=1,a=1ll*a*a%p) if(x&1) now = 1ll*now*a%p;return now;}
const int inv2 = ksm(2,p-2);
char s[101000];
int n,m;
int g[21][101000];
int a[1100000],b[1100000];
void fwt(int *a,int n,int f){
	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++){
	        	int x = a[i+j],y = a[i+j+d];
	        	a[i+j] = (x+y)%p;
	        	a[i+j+d] = (x-y+p)%p;
	        	if(f == -1) a[i+j] = 1ll*a[i+j]*inv2%p;;
	        	if(f == -1) a[i+j+d] = 1ll*a[i+j+d]*inv2%p;
			}
}
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	gi(n); gi(m);
	rep(i,1,n){
		scanf("%s",s+1);
		rep(j,1,m) g[i][j] = s[j] == '1';
	}
	int cnt;
	rep(j,1,m){
		cnt = 0; rep(i,1,n) cnt += g[i][j] ? (1<<i-1) : 0;
		a[cnt]++;
	}
	rep(s,0,(1<<n)-1) {
		cnt = 0; rep(i,1,n) cnt += (s>>(i-1)&1);
		b[s] = min(cnt,n-cnt);
	}
	fwt(a,(1<<n)-1,1); fwt(b,(1<<n)-1,1);
	rep(i,0,(1<<n)-1) a[i] = 1ll*a[i]*b[i]%p;
	fwt(a,(1<<n)-1,-1);
	int ans = n*m+1;
	rep(i,0,(1<<n)-1) ans = min(ans,a[i]);
	printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值