二分圖匹配(必須邊, 最大團)
阿新 • • 發佈:2020-12-26
題目一 國政議事(二分圖最大匹配的必須邊)
題目描述
對於任何一個高速發展的發展中國家而言,一個高效的領導小組是不可或缺的。
現在我們知道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行,每行有一個正整數,表示在最高效率的情況下,必須執行的事項的標號,按照字典序從小到大輸出。
示例1
輸入
3 3
1 2
1 3
2 3
輸出
2 2
1
3
示例2
輸入
4 5
1 4
1 3
2 4
2 3
3 1
輸出
3 1
5
思路
思路和這篇部落格一樣
考慮如下定理:若一條邊一定在最大匹配中,則在最終的殘量網路中,這條邊一定滿流,且這條邊的兩個頂點一定不在同一個強連通分量中。
#include<bits/stdc++.h> using namespace std; const int maxn = 10000 + 10; const int INF = 0x3f3f3f3f; //註釋為弧優化 struct node { int form, to, cap, flow, id, next; } edge[2000006]; int head[maxn]; int cnt; struct max_Folw { int d[maxn], cur[maxn], start, tend; bool vis[maxn]; void init(int s, int t) { memset(head, -1, sizeof(head)); cnt=0; start=s, tend=t; } void add(int start, int to, int cap, int id) { edge[cnt].form = start; edge[cnt].to = to; edge[cnt].cap = cap; edge[cnt].flow = 0; edge[cnt].id = id; edge[cnt].next = head[start]; head[start] = cnt++; } void AddEdge(int start, int to, int cap, int id) { //cout<<start<<" "<<to<<" "<<cap<<endl; add(start, to, cap, id); add(to, start, 0, id); } bool BFS() { memset(d, -1, sizeof(d)); int Q[maxn * 2]; int Thead, Ttail; Thead = Ttail = 0; Q[Ttail++] = tend; d[tend] = 0; while (Thead<Ttail) { int x = Q[Thead]; if (x == start) return true; for (int i = head[x]; i != -1; i = edge[i].next) { int temp = edge[i].to; if (d[temp] == -1 && edge[i^1].cap > edge[i^1].flow) { //沒有標記,且可行流大於0 d[temp] = d[x] + 1; Q[Ttail++] = temp; } } Thead++; } return false;//匯點是否成功標號,也就是說是否找到增廣路 } int DFS(int x, int cap) { if (x == tend) return cap; int flow = 0, f; //for (int i = cur[x]; i != -1; i = edge[cur[x]=i].next) { for (int i = head[x]; i != -1; i = edge[i].next) { int temp = edge[i].to; if (d[temp] == d[x] - 1 && edge[i].cap > edge[i].flow) { f = DFS(temp, min(cap - flow, edge[i].cap - edge[i].flow)); edge[i].flow += f; edge[i ^ 1].flow -= f; flow += f; if (flow == cap) return flow; } } d[x] = -2;//防止重搜 return flow; } int maxflow() { int flow = 0, f; while (BFS()) { //memcpy(cur, head, sizeof head); flow += DFS(start, INF); } return flow; } } flow; int scc[maxn]; struct Tarjn{ int low[maxn]; int dfn[maxn]; int vis[maxn]; int T, N=0; stack<int> s; void tarjn(int u){ low[u]=dfn[u]=++T; s.push(u), vis[u]=1; for(int i=head[u]; i!=-1; i=edge[i].next){ int to=edge[i].to; if(edge[i].cap-edge[i].flow==0) continue;//滿流不能再訪問 if(!dfn[to]){//沒有訪問過 tarjn(to); low[u]=min(low[u], low[to]); } else if(vis[to]){//在棧中 low[u]=min(low[u], dfn[to]); } } if(low[u]==dfn[u]){ ++N; while(1){ int now=s.top(); s.pop(); vis[now]=0; scc[now]=N; if(now==u){ break; } } } } }ta; vector<int> G[10005], pos[2], ans; int vis[10005]; void DFS(int u, int y){ vis[u]=y; pos[y].push_back(u); for(auto x: G[u]){ if(vis[x]==-1){ DFS(x, !y); } } } struct Node{ int x, y; }; int main() { int n, m; scanf("%d%d", &n, &m); int s=0, t=2*n+1; flow.init(s, t); for(int i=1; i<=m; i++) { int x, y, c; scanf("%d%d", &x, &y); flow.AddEdge(x, n+y, 1, i); } for(int i=1; i<=n; i++){ flow.AddEdge(s, i, 1, m+1); flow.AddEdge(n+i, t, 1, m+1); } int mx=flow.maxflow(); for(int i=1; i<=n; i++){ if(!ta.dfn[i]){//強連通縮點 ta.tarjn(i); } } for(int i=0; i<cnt; i+=2){ int x=edge[i].form, y=edge[i].to; if(x==s||x==t||y==s||y==t) continue; if(edge[i].cap-edge[i].flow==0&&scc[x]!=scc[y]){//一定是在最大流中 ans.push_back(edge[i].id); } } sort(ans.begin(), ans.end()); printf("%d %d\n", mx, ans.size()); for(int i=0; i<ans.size(); i++){ printf("%d\n", ans[i]); } return 0; } /* 5 5 1 5 1 2 5 2 3 3 3 5 3 2 4 2 4 5 2 */