二分图匹配
题目链接:luogu P3386
题目
给定一个二分图,结点个数分别为 n n n , m m m,边数为 e e e,求二分图最大匹配数
输入
第一行,
n
n
n ,
m
m
m ,
e
e
e
第二至
e
+
1
e+1
e+1行,每行两个正整数
u
u
u ,
v
v
v,表示
u
u
u ,
v
v
v有一条连边
输出
共一行,二分图最大匹配
样例输入
1 1 1
1 1
样例输出
1
数据范围
n
,
m
≤
1000
n,m \leq 1000
n,m≤1000
1
≤
u
≤
n
1 \leq u \leq n
1≤u≤n
1
≤
v
≤
m
1 \leq v \leq m
1≤v≤m
e
≤
n
×
m
e \le n\times m
e≤n×m
思路
这道题其实很简单。
我们先建图,连边,边的流量为
1
1
1。
接着,左边的点都连一个源点,右边的点都连一个汇点,流量也都是
1
1
1。
然后我们就发现这道题变成了一道最大流。
在这里我用dinic算法来做。
然后就可以了。
代码
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
struct note {
ll to, next, op, now;
}e[3000001];
ll n, m, o, x, y, le[10001], k, s, t, ans, dis[10001];
queue<int>q;
bool bfs() {
while (!q.empty()) q.pop();
memset(dis, 0x7f, sizeof(dis));
dis[s] = 0;
q.push(s);
while (!q.empty()) {
ll now = q.front();
q.pop();
for (ll i = le[now]; i; i = e[i].next)
if (dis[e[i].to] > dis[now] + 1 && e[i].now) {
dis[e[i].to] = dis[now] + 1;
if (e[i].to == t) return 1;
q.push(e[i].to);
}
}
return 0;
}
int dfs(ll now, ll an) {
if (now == t) return an;
ll go = 0;
for (ll i = le[now]; i; i = e[i].next)
if (dis[e[i].to] == dis[now] + 1 && e[i].now) {
ll line_go = dfs(e[i].to, min(e[i].now, an - go));
if (!line_go) dis[e[i].to] = -1;
e[i].now -= line_go;
e[e[i].op].now += line_go;
go += line_go;
if (go == an) break;
}
return go;
}
int main() {
scanf("%lld %lld %lld", &n, &m, &o);//输入
s = n + m + 1;//源点
t = n + m + 2;//汇点
for (ll i = 1; i <= o; i++) {
scanf("%lld %lld", &x, &y);//输入
y += n;
e[++k] = (note){y, le[x], k + 1, 1}; le[x] = k;//连边
e[++k] = (note){x, le[y], k - 1, 0}; le[y] = k;
}
for (ll i = 1; i <= n; i++) {//左边的点全部连源点
e[++k] = (note){i, le[s], k + 1, 1}; le[s] = k;
e[++k] = (note){s, le[i], k - 1, 0}; le[i] = k;
}
for (int i = n + 1; i <= n + m; i++) {//右边的点全部连汇点
e[++k] = (note){t, le[i], k + 1, 1}; le[i] = k;
e[++k] = (note){i, le[t], k - 1, 0}; le[t] = k;
}
while (bfs())//dinic算法
ans += dfs(s, 2147483647);
printf("%lld", ans);//输出
return 0;
}