SAM I AM UVA - 11419 (二分圖最小覆蓋)
阿新 • • 發佈:2018-12-26
題意:給定一個矩形,在這個矩形上有若干個東西,然後可以在每行或者每列放置一個大炮,可以把這一行或者這一列消完,現在要使用最少的大炮,問安置多少個,並且輸出位置。
題解:首先對於每個位置,要麼是放置在行的大炮消滅他要麼是列的大炮消滅他,然後根據所在的行列建兩個結點,一個行結點,一個列結點,連起來,對於剩下的位置依舊如此,然後行在左邊,列在右邊,那麼跑一個二分圖的最小點覆蓋即可。
附上程式碼:
#include<bits/stdc++.h> using namespace std; const int maxn = 1000 + 5; // 單側頂點的最大數目 // 二分圖最大基數匹配 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() { memset(left, -1, sizeof(left)); memset(right, -1, sizeof(right)); int ans = 0; for(int u = 0; u < n; u++) { // 從左邊結點u開始增廣 memset(S, 0, sizeof(S)); memset(T, 0, sizeof(T)); if(match(u)) ans++; } return ans; } // 求最小覆蓋。X和Y為最小覆蓋中的點集 int mincover(vector<int>& X, vector<int>& Y) { int ans = solve(); memset(S, 0, sizeof(S)); memset(T, 0, sizeof(T)); 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; } }; BPM solver; int R,C,N; int main() { while(scanf("%d%d%d",&R,&C,&N)==3&&R&&C&&N){ solver.init(R,C); for(int i=0;i<N;i++){ int r,c; scanf("%d%d",&r,&c); r--;c--; solver.AddEdge(r,c); } vector<int>X,Y; int ans=solver.mincover(X,Y); printf("%d",ans); for(int i=0;i<X.size();i++){ printf(" r%d",X[i]+1); } for(int i=0;i<Y.size();i++){ printf(" c%d",Y[i]+1); } printf("\n"); } return 0; }