给定 n n n个长 m m m位的二进制串
求一个二进制串 T T T,定义 v a l i val_i vali是 T T T与第 i i i个二进制串相同的位数
使得 m a x ( v a l 1 , v a l 2 . . . . v a l n ) max(val_1,val_2....val_n) max(val1,val2....valn)最小,只需要输出这个最小值。
因为总的状态只有 2 20 2^{20} 220,要是能求出每个状态到 n n n个串的 m a x max max问题就迎刃而解了
如果把这 n n n个二进制串的任何一个作为 T T T,答案是 m m m,因为和自己所有位是相同的
而且改变自己任何一位二进制作为 T T T, v a l val val值就是 m − 1 m-1 m−1
但是结合只有最大的 v a l val val有用,我们想到了多起点 b f s bfs bfs
把这 n n n个串拿去 b f s bfs bfs,谁先到状态 k k k,对 k k k而言谁就是最大的 m a x max max
这样每个状态入队一次,可以在优秀的复杂度内通过此题
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <stdio.h>
using namespace std;
const int maxn = 3e5+10;
queue<int>q;
const int inf = 1e9;
int f[1<<21],n,m;
char s[maxn][25];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",s[i] );
for(int i=0;i<=(1<<20);i++) f[i] = inf;
for(int i=1;i<=n;i++)
{
int sta = 0;
for(int j=0;j<m;j++) if( s[i][j]=='1' ) sta|=(1<<j);
f[sta] = m;
q.push( sta );
}
//让最大相似度最小
int ans = inf;
while( !q.empty() )
{
int u = q.front(); q.pop();
ans = min( ans,f[u] );
for(int i=0;i<m;i++)
{
int temp = ((1<<i)^u);
if( f[temp]==inf )
{
f[temp] = f[u]-1;
q.push( temp );
}
}
}
printf("%d",ans);
}
还有一种神仙做法二分 + F W T +FWT +FWT
首先我们把读入的串 x x x令 a [ x ] = 1 a[x]=1 a[x]=1
然后假如知道了串 T T T,令 b [ T ] = 1 b[T]=1 b[T]=1,设答案为 a n s ans ans
这样 a a a和 b b b做异或卷积得到 c c c
c i = ∑ j ⊕ k = i b j ∗ a k c_i=\sum\limits_{j\oplus k=i}b_j*a_k ci=j⊕k=i∑bj∗ak,有值的 c i c_i ci满足 i i i的二进制 0 0 0不超过 a n s ans ans
现在我们考虑逆过程,二分这个 a n s ans ans
那么给 c c c数组赋值,所有二进制不大于 a n s ans ans个 0 0 0的下标赋值为 1 1 1
让 a a a和 c c c做异或卷积可以得到数组 b b b
这个数组 b b b的任意满足 b [ i ] = n b[i]=n b[i]=n就是可行的,因为对于 b i b_i bi来说, a a a的每个元素只能造成一点贡献
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1<<23;
#define int long long
int a[maxn],b[maxn],bit[maxn],n,m,mx;
void XOR(int f[],int type)
{
for(int mid=2,k=1;mid<=mx;mid<<=1,k<<=1)
for(int i=0;i<mx;i+=mid)
for(int j=0;j<k;j++)
{
int t = i+j, v = i+j+k;
f[t] = f[t]+f[v];
f[v] = f[t]-f[v]-f[v];
if( type==-1 )
f[t]=f[t]/2, f[v]=f[v]/2;
}
}
bool isok(int x)
{
for(int i=0;i<mx;i++)
b[i] = ( (m-bit[i])<=x );//二进制0个数<=x
XOR(b,1);
for(int i=0;i<mx;i++) b[i] = a[i]*b[i];
XOR(b,-1);
for(int i=0;i<mx;i++)
if( b[i]==n ) return true;
return false;
}
signed main()
{
scanf("%lld%lld",&n,&m);
mx = (1<<m);
for(int i=0;i<mx;i++) bit[i] = bit[i>>1]+(i&1);
for(int i=1;i<=n;i++)
{
int now = 0; string s; cin >> s;
for(int j=0;j<m;j++)
if( s[j]=='1' ) now |= (1<<j);
a[now]++;
}
XOR(a,1);
int l = 0,r = m,ans=m;
while( r>=l )
{
int mid = l+r>>1;
if( isok(mid) ) r = mid-1,ans=mid;
else l = mid+1;
}
printf("%lld",ans);
}