题意:迈克有一台可以净化奶酪的机器,用二进制表示净化的奶酪的编号。但是,在某些二进制串中可能包含有至多一个‘*'。例如01*100,'*'其实就代表可以取0,1两种情况--> 010100 和011100。现在由于迈克不小心,他以同样的方式弄脏了某些奶酪,问你最少用多少次操作就可以把弄脏的奶酪全净化好。(没有被弄脏过的奶酪不能净化。弄脏过的奶酪可以多次净化。)
思路:建图想了好久,对问题的敏感性还是不够。实际上两部的选择比较显然,就是按照串中1的数量是奇还是偶来分。代码写的丑的一逼,唉。
#include <stdio.h>
#include <string.h>
#define N 2050
int n,m,k,a;
char s[12];
int p[N],q[N],used[N],link[N],g[N][N];
int dfs(int i){
int j;
for(j = 1;j<=m;j++)
if(!used[j] && g[i][j]){
used[j] = 1;
if(link[j] == -1 || dfs(link[j])){
link[j] = i;
return 1;
}
}
return 0;
}
int hungary(){
int i,res=0;
for(i = 1;i<=n;i++){
memset(used,0,sizeof(used));
if(dfs(i))
res++;
}
return res;
}
int hasstar(){//判断串中是否有*
int i;
for(i = 0;i<a;i++)
if(s[i] == '*')
return i;
return -1;
}
int isodd(){//判断串中1的个数是否为奇数个
int i,num=0;
for(i = 0;i<a;i++)
if(s[i] == '1')
num++;
return num&1;
}
int test(int x,int y){//测试两个串是否只是一位不同
int i,num=0;
x ^= y;
for(i = 0;i<a;i++)
if((1<<i) & x)
num++;
return num==1;
}
void addedge(int flag){//向二部图中添加边
int num=0,i;
for(i = 0;i<a;i++){
num += (s[i]-'0');
num <<= 1;
}
num >>= 1;
if(!flag){
for(i = 1;i<=n;i++)//看看这个点是否之前已经有了
if(num == p[i])
return;
p[++n] = num;
for(i = 1;i<=m;i++)
if(test(num , q[i]))
g[n][i] = 1;
}else{
for(i = 1;i<=m;i++)
if(num == q[i])
return;
q[++m] = num;
for(i = 1;i<=n;i++)
if(test(num,p[i]))
g[i][m] = 1;
}
}
void update(){
int j;
j = hasstar();
if(j!=-1){
if(isodd()){//有*的话,分成为1和为0两种分别讨论
s[j] = '0';
addedge(0);
s[j] = '1';
addedge(1);
}else{
s[j] = '1';
addedge(0);
s[j] = '0';
addedge(1);
}
}else{
if(isodd())
addedge(0);
else
addedge(1);
}
}
int main(){
freopen("a.txt","r",stdin);
while(scanf("%d %d\n",&a,&k) && (a+k)){
int i;
memset(g,0,sizeof(g));
memset(link,-1,sizeof(link));
n = m = 0;
for(i = 0;i<k;i++){
scanf("%s",s);
update();
}
printf("%d\n",n+m-hungary());
}
return 0;
}