众所周知,二分图的最大匹配可以用网络流来解,而且复杂度比匈牙利算法要优秀。
分析
建立虚拟源点 $S$ ,虚拟汇点 $T$ ,分别编号为 $0$,$n_1+n_2+1$ 。(只要是不重复的编号就行)
然后左半部的点连接虚拟源点 $S$ ,右半部的点连接虚拟汇点 $T$ ,容量都是 $1$ 。
再根据题意在左右部的点之间连容量为 $1$ 的边,图就建好了,最后我们在这张图跑一遍最大流即可。
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=805;
struct node{
int to, next, c;
}e[N*N+N];
int h[N], tot;
void add(int u, int v, int cap){
e[tot].to=v, e[tot].c=cap, e[tot].next=h[u], h[u]=tot++;
e[tot].to=u, e[tot].c=0, e[tot].next=h[v], h[v]=tot++;
}
int S, T;
int q[N], d[N], cur[N];
bool bfs(){
memset(d, -1, sizeof d);
int tt=-1, hh=0;
q[++tt]=S;
d[S]=0, cur[S]=h[S];
while(tt>=hh){
int hd=q[hh++];
for(int i=h[hd]; ~i; i=e[i].next){
int go=e[i].to;
if(d[go]==-1 && e[i].c){
d[go]=d[hd]+1;
cur[go]=h[go];
if(go==T) return true;
q[++tt]=go;
}
}
}
return false;
}
int find(int u, int limit){
if(u==T) return limit;
int flow=0;
for(int i=cur[u]; ~i && limit>flow; i=e[i].next){
int go=e[i].to;
cur[u]=i;
if(d[go]==d[u]+1 && e[i].c){
int t=find(go, min(e[i].c, limit-flow));
if(!t) d[go]=-1;
flow+=t, e[i].c-=t, e[i^1].c+=t;
}
}
return flow;
}
int dinic(){
int res=0, flow;
while(bfs()) while(flow=find(S, INF)) res+=flow;
return res;
}
int main(){
memset(h, -1, sizeof h);
int n1, n2, m; cin>>n1>>n2>>m;
S=0, T=n1+n2+1;
while(m--){
int u, v; cin>>u>>v;
v+=n1;
add(u, v, 1);
}
for(int i=1; i<=n1; i++) add(S, i, 1);
for(int i=1; i<=n2; i++) add(i+n1, T, 1);
cout<<dinic()<<endl;
return 0;
}
天子姐姐tql Orz
%lin
orz