题意
给定m 个长度为n 的0/1串,其中每个串至多有一位被挖掉,替换成了∗ ,表示这个位置可能是0/1。问最坏情况下最少要用多少个0/1串(也可以至多有一位为∗ )能表示这m个串
思路
一个模板可能匹配一个串或两个串,要让能匹配两个串的模板尽可能的多,建立二分图,能用一个模板匹配的串连边。答案即为实际的串数减去最大匹配数的一半(由于我们未划分二分图,所以每一组匹配会被计算两次,需要除以二)
tips:位运算小技巧:x第y位置0: x|=(1<<y);x第y位置1:x&=~(1<<y)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e3+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m;
int t[maxn];
int vis[maxn];
int G[maxn][maxn];
int match[maxn];
int f[maxn];
inline int find(int x)//匈牙利算法
{
for(int i=0;i<(1<<n);i++)
{
if(G[x][i]&&!vis[i])
{
vis[i]=1;
if(!match[i]||find(match[i]))
{
match[i]=x;
return 1;
}
}
}
return 0;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n>>m&&n&&m)
{
string s;
memset(G,0,sizeof(G));
memset(t,0,sizeof(t));
for(int i=0;i<m;i++)
{
cin>>s;
int x=0,pos=-1;
for(int j=0;j<n;j++)
{
if(s[j]=='1')
{
x+=1<<j;
}
else if(s[j]=='*')
{
pos=j;
}
}
t[x]=1;//标记有这个数
if(pos!=-1)//如果有*,则把另外一种情况也算上
t[x^(1<<pos)]=1;
}
int cnt=0;//cnt表示实际的串数
for(int i=0;i<(1<<n);i++)
{
if(t[i])
{
cnt++;
for(int j=0;j<n;j++)
{
if(t[i^(1<<j)])
{
G[i][i^(1<<j)]=1;//与和它只有一位不同的串连边
}
}
}
}
int ans=0;//最大有几个匹配数
memset(match,0,sizeof(match));
for(int i=0;i<(1<<n);i++)//匈牙利算法
{
memset(vis,0,sizeof(vis));
ans+=find(i);
}
cout<<cnt-ans/2<<endl;
}
}
二分图+二进制的小技巧
我人傻了