1. 程式人生 > 實用技巧 >二分圖匹配(必須邊, 最大團)

二分圖匹配(必須邊, 最大團)

題目一 國政議事(二分圖最大匹配的必須邊)

題目描述

對於任何一個高速發展的發展中國家而言,一個高效的領導小組是不可或缺的。
現在我們知道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
*/