题意:m个长度为n的2进制数,可能某一位是*代替(代表*=1和*=0都被包含了)。要求用最少的另外一些二进制数(也可以某一位被*代替)将原来的那些覆盖(且只能覆盖一次),且要求不能覆盖原本不存在的二进制数。
题解:由于*可以覆盖两个二进制数,所以*用的最大的时候就是题目要求的解。如果两个二进制数只相差一位,那么这两个二进制数就可以用一个带*的二进制数就可以覆盖了,由于要求不能覆盖两次,实际上就成了求二分图最大匹配。最后匹配可以覆盖掉ret*2个,剩下的就只能用单一不含*的去覆盖了。
View Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=2002; 6 bool chk[N],g[N][N]; 7 int link[N],n; 8 bool findpath(int x) 9 { 10 for(int y=0; y<n; y++) 11 { 12 if(g[x][y]&&!chk[y]) 13 { 14 chk[y]=true; 15 if(link[y]==-1||findpath(link[y])) 16 { 17 link[y]=x; 18 return true; 19 } 20 } 21 } 22 return false; 23 } 24 int maxmatch() 25 { 26 memset(link,-1,sizeof(link)); 27 int ret=0; 28 for(int x=0; x<n; x++) 29 { 30 memset(chk,false,sizeof(chk)); 31 if(findpath(x)) 32 ret++; 33 } 34 return ret; 35 } 36 struct data 37 { 38 char s[N]; 39 bool operator<(const data &ne)const 40 { 41 return strcmp(s,ne.s)<=0; 42 } 43 }po[N]; 44 bool check(char s1[],char s2[]) 45 { 46 int ret=0; 47 for(int i=0; s1[i]!='\0'; i++) 48 if(s1[i]!=s2[i]) 49 ret++; 50 return ret==1; 51 } 52 int main() 53 { 54 int nn,mm; 55 while(scanf("%d%d",&nn,&mm),(nn||mm)) 56 { 57 n=0; 58 for(int i=0; i<mm; i++) 59 { 60 scanf("%s",po[n].s); 61 for(int j=0; j<nn; j++) 62 { 63 if(po[n].s[j]=='*') 64 { 65 strcpy(po[n+1].s,po[n].s); 66 po[n].s[j]='0'; 67 po[n+1].s[j]='1'; 68 n++; 69 break; 70 } 71 } 72 n++; 73 } 74 sort(po,po+n); 75 mm=0; 76 for(int i=1;i<n;i++) 77 { 78 if(strcmp(po[mm].s,po[i].s)<0) 79 strcpy(po[++mm].s,po[i].s); 80 } 81 n=mm+1; 82 memset(g,false,sizeof(g)); 83 for(int i=0; i<n; i++) 84 { 85 for(int j=0; j<n; j++) 86 { 87 if(check(po[i].s,po[j].s)) 88 g[i][j]=true; 89 } 90 } 91 mm=maxmatch(); 92 printf("%d\n",n-mm+mm/2); 93 } 94 return 0; 95 }