1. 程式人生 > >Purifying Machine UVA - 1663 (二分圖最大匹配)

Purifying Machine UVA - 1663 (二分圖最大匹配)

傳送門

題意:給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;
}