UVA1663 净化器 Purifying Machine / YbtOJ「图论」第1章 二分图匹配 I. 模板集合 题解--zhengjun
思路
发现模板串最多只有一个*
,所以考虑对于一个含有*
的模板串,相当于链接了两个不含*
的模板串。
所以我们先把所有的串转换为不含*
的模板串,然后两两判断是否可以连边(因为只能有一个*
,所以判断依据就是 \(\operatorname{popcount}(u \operatorname{xor} v)=1\))。
这时候我们已经把原问题转化为了图上问题,此时就是要求选出最少的边使得这些边覆盖所有的点。
然而你会发现这张图是个二分图,一边是 \(\operatorname{popcount}(u)\) 为奇数的,另一边是偶数的,因为只有一位不一样的话一定会使得 \(\operatorname{popcount}\) 的奇偶性发生变化。
最后发现,每连接一对匹配,都会使得总的答案数减一,所以只要求出最大匹配即可求出答案。
代码
#include<bits/stdc++.h>
using namespace std;typedef long long ll;const int N=15,M=(1<<10)+10,E=M*M;
int n,m,s,t,is[M],head[M],kk,d[M],cur[M],cnt[M];char a[N];struct edges{int to,c,nex;}edge[E];
void add(int u,int v,int c){edge[++kk]={v,c,head[u]};head[u]=kk;edge[++kk]={u,0,head[v]};head[v]=kk;}
void insert(){int x=0,y=0;for(int i=1;i<=n;i++)x<<=1,y<<=1,a[i]!='0'&&x++,a[i]=='1'&&y++;is[x]=is[y]=1;}
bool chk(int x,int y){return is[x]&&is[y]&&!((x^y)&((x^y)-1));}
bool bfs(){
queue<int>q;memset(d,-1,sizeof d);d[s]=0;cur[s]=head[s];for(q.push(s);!q.empty();q.pop()){
int u=q.front();for(int i=head[u],v;v=edge[i].to,i;i=edge[i].nex)
if(d[v]==-1&&edge[i].c)q.push(v),cur[v]=head[v],d[v]=d[u]+1;
}return d[t]!=-1;
}
int dfs(int u,int lim=1e9){
if(u==t)return lim;int flow=0;for(int i=head[u],v;v=edge[i].to,flow<lim&&i;i=edge[i].nex){
if(d[v]!=d[u]+1||!edge[i].c)continue;int f=dfs(v,min(lim-flow,edge[i].c));
if(!f)d[v]=-1;edge[i].c-=f;edge[i^1].c+=f;flow+=f;
}return flow;
}
int dinic(){int maxflow=0;while(bfs())maxflow+=dfs(s);return maxflow;}
void clear(){memset(head,0,sizeof head);kk=1;memset(is,0,sizeof is);}
void get(){
for(int i=1;i<=m;i++)scanf("%s",a+1),insert();m=0;for(int i=0;i<(1<<n);i++)if(is[i])m++;
for(int i=0;i<(1<<n);i++)for(int j=i+1;j<(1<<n);j++)if(chk(i,j))cnt[i]&1?add(i,j,1):add(j,i,1);
s=1<<n;t=(1<<n)+1;for(int i=0;i<(1<<n);i++)if(cnt[i]&1)add(s,i,1);else add(i,t,1);printf("%d\n",m-dinic());
}
void init(){for(int i=0;i<(1<<10);i++)cnt[i]=cnt[i>>1]+(i&1);}
int main(){
for(init();~scanf("%d%d",&n,&m)&&(n||m);)clear(),get();return 0;
}