牛客練習賽30: E. 國政議事(二分匹配)
阿新 • • 發佈:2018-12-19
題目描述
對於任何一個高速發展的發展中國家而言,一個高效的領導小組是不可或缺的。
現在我們知道k國的領導小組有n個人,準備舉行一次會議,他們一共需要處理m個重要事項,第i個重要事項在ai手中,並且該重要事項需要交給bi來具體實施。
人都到齊後,他們會進行一個“交換意見”的環節,即每個人都會把自己手中一個自己認為關鍵的事項i的相關材料轉發給該事項的具體實施者bi(如果該人手中沒有重要事項,則不進行操作),隨後,每個人都從自己收到的重要事項中選擇一個自己認為關鍵的去實施,每實施一個事項,可以獲得1點效率。
很顯然,領導小組希望在這次會議中的效率更高,請你幫助他們決定在效率最高的情況下,哪些事項是必須執行的。
輸入描述:
第一行兩個正整數n(n<=500),m(m<=20000); 接下來m行,第i+1行兩個正整數ai和bi表示重要事項i在ai手中,並且需要交給bi具體實施,可能存在ai=bi的情況
輸出描述:
第一行一個正整數ans,num表示該會議的最高效率和必須執行的事項個數; 接下來num行,每行有一個正整數,表示在最高效率的情況下,必須執行的事項的標號,按照字典序從小到大輸出。
輸入
3 3 1 2 1 3 2 3
輸出
2 2 1 3
一個人只能轉手一次材料 → 二分圖左邊的每個點只能選一次
一個人只能實施一次計劃 → 二分圖右邊的每個點只能選一次
一份計劃經過轉手+實施效率+1 → 選中一條邊+1效率
所以這道題就是個二分匹配
將每個人拆成兩個點,一個點表示轉手(左),一個點表示實施(右), 對於第i個計劃ai, bi,左邊第ai個點向右邊第bi個點連邊
求出最大匹配就是第一問的答案,很簡單
而第二問相當於是有多少條邊你刪掉它之後,最大匹配會-1,輸出這些邊
考慮直接暴力刪除每一條邊,每次求一遍最大匹配,複雜度O(nm²), 會超時
仔細分析一下可以發現不需要暴力每條邊,只要先求一次二分匹配,然後暴力最大匹配的那些邊即可,複雜度O(n²m)
除此之外二分匹配可以通過預處理增廣路優化成O(nmsqrt(n)),加上其實跑不滿,完全可過
#pragma comment(linker, "/STACK:102400000,102400000") #include<stdio.h> #include<string.h> #include<algorithm> #include<map> #include<string> #include<math.h> #include<queue> #include<stack> #include<iostream> using namespace std; #define LL long long #define mod 1000000007 vector<int> G[505], F[505], B; int ban, n, m, dis, lx[505], ly[505], dx[505], dy[505], vis[505], q[20005]; int HKSech(); int Sech(int u); int main(void) { int u, v, i, Ans, p, ans, cnt; scanf("%d%d", &n, &m); for(i=1;i<=m;i++) { scanf("%d%d", &u, &v); G[u].push_back(v); F[u].push_back(i); } m = n; cnt = Ans = ban = 0; memset(lx, -1, sizeof(lx)); memset(ly, -1, sizeof(ly)); while(HKSech()) { memset(vis, 0, sizeof(vis)); for(i=1;i<=n;i++) { if(lx[i]==-1 && Sech(i)) Ans++; } } for(i=1;i<=n;i++) { if(lx[i]!=-1) B.push_back(lx[i]); } for(p=0;p<B.size();p++) { ans = 0, ban = B[p]; memset(lx, -1, sizeof(lx)); memset(ly, -1, sizeof(ly)); while(HKSech()) { memset(vis, 0, sizeof(vis)); for(i=1;i<=n;i++) { if(lx[i]==-1 && Sech(i)) ans++; } } if(ans!=Ans) q[++cnt] = ban; } printf("%d %d\n", Ans, cnt); sort(q+1, q+cnt+1); for(i=1;i<=cnt;i++) printf("%d\n", q[i]); return 0; } int HKSech() { int i, x, v; dis = 100000000; queue<int> q; memset(dx, -1, sizeof(dx)); memset(dy, -1, sizeof(dy)); for(i=1;i<=n;i++) { if(lx[i]==-1) { q.push(i); dx[i] = 0; } } while(q.empty()==0) { x = q.front(); q.pop(); if(dx[x]>dis) break; for(i=0;i<G[x].size();i++) { v = G[x][i]; if(F[x][i]==ban) continue; if(dy[v]==-1) { dy[v] = dx[x]+1; if(ly[v]==-1) dis = dy[v]; else { dx[ly[v]] = dy[v]+1; q.push(ly[v]); } } } } if(dis==100000000) return 0; return 1; } int Sech(int x) { int i, v; for(i=0;i<G[x].size();i++) { v = G[x][i]; if(F[x][i]==ban) continue; if(vis[v]==0 && dy[v]==dx[x]+1) { vis[v] = 1; if(ly[v]!=-1 && dy[v]==dis) continue; if(ly[v]==-1 || Sech(ly[v])) { ly[v] = x; lx[x] = F[x][i]; return 1; } } } return 0; }