题目链接:
http://poj.org/problem?id=2724
题目大意:
有2^N个奶酪,编号从000…00到111…11,现在有台机器有N个开关,每个开关的状态有3个,
分别是‘0‘、‘1‘、‘*‘,每个开关只能有一个状态存在。‘*‘状态可以替代‘0‘或‘1‘状态。比如11*1,
对应为1111或1101。现在有M个奶酪被感染了,每个奶酪的编号为长度为N的二进制字符串。
和开关一样,每一位上除了能为‘1‘、‘0‘之外,还可以是‘*‘,表示既能是‘0‘,也能是‘1‘。比如说
1*1,既可以是101,也可以是111。现在要把这些被感染的奶酪(二进制字符串)都删除掉。删除
的方法有两种:
1)通过和被感染的奶酪编号一样的机器一次删除指定的一个字符串,比如说用111删除111。
2)如果有两个串只有一个字符不同,则可以用带‘*‘的字符串一次性同时删除这两个字符串(其中包
括重复的串),比如说用1*1同时删除101和111。
机器每删除一次,就要转换一次状态再进行下一次删除。问:机器最少要转换多少次状态才能将
所有串删除完。
思路:
将所有的二进制串都变为只包含01的二进制串,然后用数组Num[]来存储二进制串对应的十进制
数,比如说101对应十进制就是5。然后对Num[]数组排序,删除所有重复的数。这样子,就可以
用点来表示字符串了。假设有cnt个无重复数的点(二进制串)。然后,建立二分图,每边都是cnt个
点。对于所有满足二进制形式只差一位的两个点进行双向建边。最后问题就变为了:求二分图最小
边覆盖问题。二分图最小边覆盖 = 点数cnt - 二分图最大匹配/2。用匈牙利算法求二分图最大匹配
即可。
对于判断数A和数B的二进制形式是否只差一位,可通过 t = A^B,然后判断(t && (t&(t-1))==0),
满足,则A和B的二进制形式只差一位。
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int MAXN = 2050; bool Map[MAXN][MAXN],Mask[MAXN]; int NX,NY; int cx[MAXN],cy[MAXN]; int FindPath(int u) { for(int i = 1; i <= NY; ++i) { if(Map[u][i] && !Mask[i]) { Mask[i] = 1; if(cy[i] == -1 || FindPath(cy[i])) { cy[i] = u; cx[u] = i; return 1; } } } return 0; } int MaxMatch() { for(int i = 1; i <= NX; ++i) cx[i] = -1; for(int i = 1; i <= NY; ++i) cy[i] = -1; int res = 0; for(int i = 1; i <= NX; ++i) { if(cx[i] == -1) { for(int j = 1; j <= NY; ++j) Mask[j] = 0; res += FindPath(i); } } return res; } char s[12]; int Num[MAXN]; int main() { int N,M; while(~scanf("%d%d",&N,&M) && (N||M)) { memset(Map,0,sizeof(Map)); memset(Num,0,sizeof(Num)); int cnt = 0,k; for(int i = 1; i <= M; ++i) { scanf("%s",s); cnt++; k = -1; for(int j = 0; j < N; ++j) { if(s[j] == '*') k = j; else Num[cnt] |= ((s[j]-'0')<<j); } if(k != -1) { cnt++; Num[cnt] = (Num[cnt-1] | (1 << k)); } } sort(Num+1,Num+1+cnt); Num[0] = -1; int j = 0; for(int i = 1; i <= cnt; ++i) if(Num[j] != Num[i]) Num[++j] = Num[i]; cnt = j; for(int i = 1; i <= cnt; ++i) { for(int j = i+1; j <= cnt; ++j) { int t = Num[i]^Num[j]; if(t && (t&(t-1))==0) Map[i][j] = Map[j][i] = 1; } } NX = NY = cnt; int Max = MaxMatch(); printf("%d\n",cnt - Max/2); } return 0; }