Purifying Machine UVA - 1663 (二分圖最大匹配)
阿新 • • 發佈:2018-12-26
題意:給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; }