题意:给m个长度为n的模板串,每个模板串包含字符0,1和最多一个星号“*“,其中星号可以匹配0或1。改写这个模板集合,使得模板的个数最少。也就是一个*可以匹配两个串
题解:两两判断,如果二者的二进制只有一位不同,在s1和s2之间连一条边,对应一个带"*”的模板串,其中“*”的位置就是s1和s2不同的那一位,而s1和s2中1的个数的奇偶性必然不同,可以按照这个性质将所有点分为两类,就形成了一个二分图。这个二分图的每一个匹配都对应一个带"*"的模板集合。求此二分图的最大匹配,假设其中有n条边,则所求的模板集合的最小值就是|S|-n.
附上代码:
#include<bits/stdc++.h>
#define _for(i,a,b) for( int i=(a); i<(b); ++i)
#define _rep(i,a,b) for( int i=(a); i<=(b); ++i)
using namespace std;
int countBit(int x, int w){
int b = 1, ans = 0;
_for(i, 0, w) ans += ((b&x)!=0), b <<= 1;
return ans;
}
string printbin(int x, int w){
string buf;
for(int i = w-1; i >= 0; i--) buf += (((1<<i)&x) > 0 ? 1 : 0) + '0';
return buf;
}
// 二分图最大基数匹配
template<int maxn>
struct BPM {
int n, m; // 左右顶点个数
vector<int> G[maxn]; // 邻接表
int left[maxn]; // left[i]为右边第i个点的匹配点编号,-1表示不存在
bool T[maxn]; // T[i]为右边第i个点是否已标记
int right[maxn]; // 求最小覆盖用
bool S[maxn]; // 求最小覆盖用
void init(int n, int m) {
this->n = n;
this->m = m;
for(int i = 0; i < n; i++) G[i].clear();
}
void AddEdge(int u, int v) {
G[u].push_back(v);
}
bool match(int u){
S[u] = true;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (!T[v]){
T[v] = true;
if (left[v] == -1 || match(left[v])){
left[v] = u;
right[u] = v;
return true;
}
}
}
return false;
}
// 求最大匹配
int solve() {
fill_n(left, maxn, -1);
fill_n(right, maxn, -1);
int ans = 0;
for(int u = 0; u < n; u++) { // 从左边结点u开始增广
fill_n(S, sizeof(S), 0);
fill_n(T, sizeof(T), 0);
if(match(u)) ans++;
}
return ans;
}
// 求最小覆盖。X和Y为最小覆盖中的点集
int mincover(vector<int>& X, vector<int>& Y) {
int ans = solve();
fill_n(S, maxn, 0);
fill_n(T, maxn, 0);
for(int u = 0; u < n; u++)
if(right[u] == -1) match(u); // 从所有X未盖点出发增广
for(int u = 0; u < n; u++)
if(!S[u]) X.push_back(u); // X中的未标记点
for(int v = 0; v < m; v++)
if(T[v]) Y.push_back(v); // Y中的已标记点
return ans;
}
};
const int MAXM = 1024 + 5;
BPM<MAXM> solver;
string S[MAXM];
int N, M, Set[MAXM];
int main(){
string buf;
set<int> vs; // verticles
while(true){
cin>>N>>M;
if(N == 0) break;
int sz = 1<<N;
fill_n(Set, sz, 0);
solver.init(sz, sz);
_for(i, 0, M){
cin>>buf;
int x = -1, v = 0, bit = 1;
_for(j, 0, buf.size()) {
char c = buf[j];
if(c == '*') x = j, bit = 1;
else bit = c - '0';
v = v*2 + bit;
}
Set[v] = 1;
if(x != -1) {
v &= ~(1<<(N-x-1));
Set[v] = 1;
}
}
// 左边是偶数个1的串,右边是奇数个1的模板串
int cnt = 0;
_for(i, 0, sz) {
if(!Set[i]) continue;
// cout<<printbin(i, N)<<", ";
cnt++;
if(countBit(i, N)%2 == 1) continue;
_for(b, 0, N){
int j = (1<<b)^i;
if(Set[j]) solver.AddEdge(i, j);
}
}
int m = solver.solve();
cout<<cnt-solver.solve()<<endl;
}
return 0;
}