最小相似度
思路不错的题目
考虑到总体的状态数只有\(2^m\)中,如果我们把某个串改变1位,这个改变后的串和这个串的答案就是\(m-1\),由此可见,每个串到另一个串都有一个距离,我们把这个距离设为改变的位数,所有串到某个串都有一个最小位数\(x\),这个最小位数的最大值\(y\)的答案\(m-y\)就是我们的答案。
我们发现这就是一个最短路,直接拿01bfs实现,复杂度\(O(n+m2^m)\)
然后还有一种\(FWT\)的做法
注意到\(A \ xor \ B=C\Rightarrow B=A \ xor \ C\)
然后我们枚举或者二分答案为\(x\)后\(C\)就是确定的了,就是\(C_i=[f(i)<=x]\),\(f(i)\)表示二进制串下\(1\)的个数
然后\(A\)卷\(C\),如果存在\(B_i=n\)就说明所以的串都卷上去了,这个答案就合法
Code:(BFS):
#include <cstdio>
#include <cctype>
#include <cstring>
template <class T>
void read(T &x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
const int N=1<<20;
int q[N+10],dp[N],l=1,r;
int n,m;
char s[23];
int main()
{
memset(dp,-1,sizeof dp);
read(n),read(m);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
int sta=0;
for(int j=1;j<=m;j++)
sta|=(s[j]=='1')<<j-1;
dp[sta]=0;
}
for(int i=0;i<1<<m;i++)
if(~dp[i])
q[++r]=i;
int ans=0;
while(l<=r)
{
int now=q[l++];
ans=ans>dp[now]?ans:dp[now];
for(int i=0;i<m;i++)
{
int v=now^(1<<i);
if(dp[v]==-1)
dp[q[++r]=v]=dp[now]+1;
}
}
printf("%d\n",m-ans);
return 0;
}
Code(FWT):
#include <cstdio>
#include <cctype>
template <class T>
void read(T &x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
const int mod=998244353,inv=499122177;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
#define mul(a,b) (1ll*(a)*(b)%mod)
const int N=1<<20;
int n,m,A[N],B[N],d[N];
char s[23];
void FWT(int *a,int len,int typ)
{
for(int le=1;le<len;le<<=1)
for(int p=0;p<len;p+=le<<1)
for(int i=p;i<p+le;i++)
{
int x=a[i],y=a[i+le];
a[i]=add(x,y),a[i+le]=add(x,mod-y);
if(!typ) a[i]=mul(a[i],inv),a[i+le]=mul(a[i+le],inv);
}
}
bool check(int x)
{
for(int i=0;i<1<<m;i++) B[i]=d[i]<=x;
FWT(B,1<<m,1);
for(int i=0;i<1<<m;i++) B[i]=mul(A[i],B[i]);
FWT(B,1<<m,0);
for(int i=0;i<1<<m;i++)
if(B[i]==n)
return true;
return false;
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
int sta=0;
for(int j=1;j<=m;j++)
sta|=(s[j]=='1')<<j-1;
++A[sta];
}
FWT(A,1<<m,1);
for(int i=1;i<1<<m;i++) d[i]=d[i>>1]+(i&1);
int l=0,r=m;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
return 0;
}
2019.3.2