P3386 【模板】二分图匹配 Ek 与 dinic

来源:互联网 发布:为什么网络主播那么多 编辑:程序博客网 时间:2024/06/08 03:18

题目背景

二分图

题目描述

给定一个二分图,结点个数分别为n,m,边数为e,求二分图最大匹配数

输入输出格式

输入格式:

第一行,n,m,e

第二至e+1行,每行两个正整数u,v,表示u,v有一条连边

输出格式:

共一行,二分图最大匹配

输入输出样例

输入样例#1: 复制
1 1 11 1
输出样例#1: 复制
1

说明  n,m10001≤u≤n 1vm

因为数据有坑,可能会遇到 v>mv>mv>m 的情况。请把 v>mv>mv>m 的数据自觉过滤掉。

算法:二分图匹配

通过引一个源点 到 n 的所有点和m的所有点到一个汇点,将二分图转化,这个新图的最大流(即不存在增广路时)就等于二分图的最大匹配。

这里m没给定,但显然是略大的(>10000)所以用O(V*E*E)的EK算法超时了....

这里是EK算法的代码

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>#include<queue>using namespace std;const int MAXN = 1000;const int F = 2010;const int INF = 1E9;typedef long long LL;typedef double DB;inline int get(){char c;while((c = getchar()) < '0' || c > '9');int cnt = c - '0';while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';return cnt;}int N,M,E;struct edge{int fr,to,f;edge(int u,int v,int w):fr(u),to(v),f(w){}};vector<edge> e;vector<int> g[2 * MAXN + 20];int a[2 * MAXN + 20];int pa[2 * MAXN + 20];int maxf = 0;inline void zdl(int s,int t){while(1){memset(a,0,sizeof(a));a[s] = INF;queue<int> q;q.push(s);while(!q.empty()){int f = q.front(); q.pop();for(int i = 0; i < g[f].size(); i ++){edge k = e[g[f][i]];if(!a[k.to] && k.f > 0){a[k.to] = 1;pa[k.to] = g[f][i];q.push(k.to);}if(a[t]) break;}if(a[t]) break;}if(!a[t]) break;for(int i = t; i != s; i = e[pa[i]].fr){e[pa[i]].f -= 1;e[pa[i] ^ 1].f += 1;}maxf += a[t];}return;}int main(){#ifdef lwyfreopen("1.txt","r",stdin);/*#elsefreopen(".in","r",stdin);freopen(".out","w",stdout);*/ #endifN = get(); M = get(); E = get();for(int i = 1; i <= E; i ++){int u,v;u = get(); v = get();if(u > N || v > M) continue;v += MAXN;e.push_back(edge(u,v,1));e.push_back(edge(v,u,0));int m = e.size();g[v].push_back(m - 1);g[u].push_back(m - 2);} for(int i = 1; i <= N; i ++){e.push_back(edge(0,i,1));e.push_back(edge(i,0,0));int m = e.size();g[i].push_back(m - 1);g[0].push_back(m - 2);}for(int i = 1; i <= M; i++){e.push_back(edge(i + MAXN,F,1));e.push_back(edge(F,i + MAXN,0));int m = e.size();g[F].push_back(m - 1);g[i + MAXN].push_back(m - 2);}zdl(0,F);printf("%d",maxf);return 0;}

由于二分图需要引一个超级源点和一个超级汇点,所以这个图有可能是稠密图,这样Ek算法的O(V * E *E)就显得非常吃力了....

所以要用dinic算法 

可以证明dinic算法在二分图的复杂度是O(根号V * E),这样可以轻松跑过大的点了。

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>#include<queue>using namespace std;const int MAXN = 2000;const int INF = 1E9;typedef long long LL;typedef double DB;inline int get(){    char c;    while((c = getchar()) < '0' || c > '9');    int cnt = c - '0';    while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';    return cnt;}int N,M,E,S,T;int maxf = 0;struct edge{    int fr,to,fl;    edge(int u,int v,int w): fr(u),to(v),fl(w){}};vector<edge> e;vector<int> g[MAXN + 10];int dep[MAXN + 10];bool vis[MAXN + 10];int cur[MAXN + 10];inline bool bfs(){    memset(vis,false,sizeof(vis));    memset(dep,0,sizeof(dep));    vis[S] = true;     queue<int> q;    q.push(S);     while(!q.empty()){        int f = q.front(); q.pop();        for(int i = 0; i < g[f].size(); i ++){            edge k = e[g[f][i]];            if(!vis[k.to] && k.fl > 0){ // k.fl                dep[k.to] = dep[f] + 1;                vis[k.to] = true;                q.push(k.to);            }        }    }    return vis[T];}inline int dfs(int x,int a){    if(x == T || a == 0) return a;    int flowt = 0,flown; // flowt = 0    for(int& i = cur[x]; i < g[x].size(); i ++){        edge k = e[g[x][i]];        if(dep[k.to] == dep[x] + 1){            int flown = dfs(k.to,min(a,k.fl));            if(flown > 0){                e[g[x][i]].fl -= flown;                e[g[x][i] ^ 1].fl += flown;                flowt += flown;                a -= flown;                if(a == 0) break;            }        }    }    return flowt;}int main(){    #ifdef lwy        freopen("1.txt","r",stdin);/*#else        freopen(".in","r",stdin);        freopen(".out","w",stdout);*/     #endif    N = get(); M = get(); E = get();    S = 0; T = N + M + 1;    for(int i = 1; i <= E; i ++){        int u,v;        u = get(); v = get(); v += N;        if(u > N || v > M + N) continue;        e.push_back(edge(u,v,1));        e.push_back(edge(v,u,0));        int m = e.size();        g[v].push_back(m - 1);        g[u].push_back(m - 2);    }    for(int i = 1; i <= N; i ++){        e.push_back(edge(0,i,1));        e.push_back(edge(i,0,0));        int m = e.size();        g[i].push_back(m - 1);        g[0].push_back(m - 2);    }    for(int i = N + 1; i <= N + M; i ++){        e.push_back(edge(i,T,1));        e.push_back(edge(T,i,0));        int m = e.size();        g[T].push_back(m - 1);        g[i].push_back(m - 2);    }    while(bfs()){        memset(cur,0,sizeof(cur));        maxf += dfs(S,INF);    }    printf("%d",maxf);    return 0;}


原创粉丝点击