http://poj.org/problem?id=2724
描述
迈克是奶酪工厂的老板。他有2^N个奶酪,每个奶酪都有一个00 ... 0到11 ... 1的二进制数。为了防止他的奶酪免受病毒侵袭,他用一台净化机器来清理感染病毒的奶酪。净化机有N个开关,每个开关有三个状态,1,0和*。一次操作中,最多可用*一次,它可以代替1或0.当机器转到一个特定的状态时,操作将清除所有相应的二进制数字的奶酪。
有一天,迈克的机器被感染了。当麦克发现时,他已经做了一些操作,这台受感染机器操作的奶酪也被感染了。他尽可能快地清洗机器,现在他需要用最少的操作次数来清理被感染的奶酪。如果奶酪被感染,用机器清洗这个奶酪一次或多次将使这种奶酪再次免于病毒;但是如果一个奶酪没有被感染,这个奶酪的操作会使它变坏。
现在已知Mike已经完成的感染操作,你需要找出清理所有受感染的奶酪所需执行的最少操作次数。
输入
有几个测试用例。每个测试用例都包含两个数字N和M(1 <= N <= 10,1 <= M <= 1000)的行开始。以下M行中的每一行都包含机器的开关状态。 N = M = 0的测试用例结束输入,不应该被处理。
输出
对于每个测试用例,输出一行包含一个整数,这是Mike需要做的最小操作数。
示例输入
3 3
* 01
100
011
0 0
示例输出
2
————————————————————
因为题面很麻烦所以谷歌+手动翻译了一下。
首先将原串展开判重离散化,然后二分图最小边覆盖即可。
注意边是双向边所以用一种简单的方法解决最小边覆盖。
我们不拆点,而是连两条边(例如i到j有一条边,则A集合i连B集合j且B集合i连A集合j)
最后匹配数/2即可。
#include<cstdio> #include<cstring> #include<cctype> #include<iostream> #include<map> using namespace std; const int maxn=2001; map<int,bool>mp; int t[maxn],cnt=0,m,n; bool vis[maxn],a[maxn][maxn]={0}; int shu[maxn]={0}; char s[50]; bool dfs(int i){ for(int j=1;j<=cnt;j++){ if(a[i][j]&&!vis[j]){ vis[j]=1; if(!shu[j]||dfs(shu[j])){ shu[j]=i; return 1; } } } return 0; } void read(){ cin>>s; int sum=0; for(int i=0;i<n;i++){ if(s[i]=='*')sum=sum*2+0; else sum=sum*2+s[i]-'0'; } if(!mp[sum]){ cnt++;mp[sum]=1;t[cnt]=sum; } sum=0; for(int i=0;i<n;i++){ if(s[i]=='*')sum=sum*2+1; else sum=sum*2+s[i]-'0'; } if(!mp[sum]){ cnt++;mp[sum]=1;t[cnt]=sum; } return; } int main(){ while(cin>>n>>m){ if(n==0&&m==0)break; memset(a,0,sizeof(a)); mp.clear();cnt=0; for(int i=1;i<=m;i++)read(); for(int i=1;i<=cnt;i++){ for(int j=i+1;j<=cnt;j++){ int k=t[i]^t[j]; if(k&&(k&(k-1))==0)a[i][j]=a[j][i]=1; } } int ans=0; memset(shu,0,sizeof(shu)); for(int i=1;i<=cnt;i++){ memset(vis,0,sizeof(vis)); if(dfs(i))ans++; } printf("%d ",cnt-ans/2); } return 0; }