题意:给出m串长度为n的01串。有些串中可能包含*,这样的串可以表示两个串,*为1 和*为0。重复的算一种。现在我们需要消灭掉所有的01串,消灭方式有两种:
1:一次消灭一个串。
2:如果两个串的差别只有一位的话可以同时消灭这两个串。
问最少多少次操作可以消灭所有的01串
思路:也就是给你一些不同的(判重之后)二进制串,每个串可以通过1次操作消去,也可以把两个只有1位不同的串通过1次操作一起消去.要我们求最少的操作次数.可以把所有串按其中1的个数和是奇还是偶分成左右两个点集.对于任意两个串,如果他们只有1位不同,那么就在他们之间连接一条无向边.(这两个串一定分别属于不同的点集),因为串的个数是固定的,并且一个串可以由单独的消去或者联合消去,而要让操作的次数最少,是不是可以联想到那道基站覆盖信号的题?其实是异曲同工的,需要操作的次数最少,那自然联合消去的次数要最多,那么只要求出这个最大值,然后n减去就是答案了,显然求出这个值用的最大匹配,当然也可以不用把串特意分成左右点集,我们只需要把原图翻倍,然后求翻倍图的最大匹配数ans,最后用n-ans/2即可.类似于POJ1466
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
#include<iostream>
#include<string>
using namespace std;
const int maxn=1000+5;
struct Max_Match
{
int n,m;//左右点集大小,点从1开始编号
vector<int> g[maxn];//g[i]表示左边第i个点邻接的右边点的集合
bool vis[maxn];//vis[i]表示右边第i个点是否在本次match中被访问过
int left[maxn];//left[i]==j表右边第i个点与左边第j个点匹配,为-1表无点匹配
void init(int n)
{
this->n=n;
// this->m=m;
for(int i=1;i<=n;i++) g[i].clear();
memset(left,-1,sizeof(left));
}
//判断从左u点是否可以找到一条增广路
bool match(int u)
{
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(!vis[v])
{
vis[v]=true;
if(left[v]==-1 || match(left[v]))//找到增广路
{
left[v]=u;
return true;
}
}
}
return false;
}
//返回当前二分图的最大匹配数
int solve()
{
int ans=0;//最大匹配数
for(int i=1;i<=n;i++)//每个左边的节点找一次增广路
{
memset(vis,0,sizeof(vis));
if(match(i)) ans++;//找到一条增广路,形成一个新匹配
}
return ans;
}
}MM;
struct Node
{
string s;
}node[maxn];
bool check(int i,int j)
{
int num = 0;
for (int ii = 0;ii<node[i].s.size();ii++)
{
if (node[i].s[ii] != node[j].s[ii])
num++;
}
return num == 1;
}
int main()
{
int n,m;
while (scanf("%d%d",&n,&m)!=EOF && n)
{
int num = 0;
set<string> s;
for (int i = 1;i<=m;i++)
{
string temp;
cin >> temp;
if (temp.find("*") != -1)
{
int pos = temp.find("*");
string s1(temp),s2(temp);
s1[pos]='0'; //*号填充为0
s2[pos]='1'; //*号填充为1
if (s.find(s1) == s.end())
{
s.insert(s1);
node[++num].s = s1;
}
if (s.find(s2) == s.end())
{
s.insert(s2);
node[++num].s = s2;
}
}
else
{
if (s.find(temp) == s.end())
{
s.insert(temp);
node[++num].s = temp;
}
}
}
MM.init(num);
for (int i = 1;i<=num;i++)
for (int j = 1;j<=num;j++)
{
if (i!=j && check(i,j))
MM.g[i].push_back(j);
}
printf("%d\n",num-MM.solve()/2);
}
}