题意
有 N N 个点,其中编号为 的点可以和编号为 M+1−N M + 1 − N 的点匹配。
已知所有允许的匹配点对 (a,b) ( a , b ) ,求最大匹配数,并输出配对方案。
如果不存在,输出No Solution!
。
数据范围
1≤M≤N≤100 1 ≤ M ≤ N ≤ 100
题解
裸的二分图最大匹配。
考虑匈牙利算法,复杂度 O(n×m) O ( n × m ) ,复杂度有点高(虽然也能跑过)。
考虑Hopcroft-Karp算法,复杂度 O(n−−√×m) O ( n × m ) ,但是我不会写。
考虑最大流算法。
根据陈胤伯的集训队论文《浅谈图的匹配算法及其应用》,得知利用Dinic算法进行二分图最大匹配的复杂度亦为 O(n−−√×m) O ( n × m ) ,遂选择之。
建图方法很简单,新建源点 S S 和汇点 , S S 、 分别与二分图的两部分的每个点连接流量限度为 1 1 的边, 可以匹配的点对 亦连接流量限制为 1 1 的边。跑得 最大流即原二分图最大匹配数。
至于匹配方案,根据残余网络,输出所有剩余流量为 0 0 且不与 相连的边的点对即可。
参考代码
// Copyright 2018, Skqliao
#include <bits/stdc++.h>
#define rg register
#define rep(i, l, r) for (rg int i = (l), _##i##_ = (r); i < _##i##_; ++i)
#define rof(i, l, r) for (rg int i = (l) - 1, _##i##_ = (r); i >= _##i##_; --i)
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) static_cast<int>((x).size())
typedef long long ll;
namespace io {
#ifndef ONLINE_JUDGE
char gc() {
return getchar();
}
template <class T> inline T gt() {
register T x;
std::cin >> x;
return x;
}
#else
const int MAXSIZE = 1 << 22;
inline char gc() {
static char In[MAXSIZE], *at = In, *en = In;
if (at == en) {
en = (at = In) + fread(In, 1, MAXSIZE, stdin);
}
return at == en ? EOF : *at++;
}
template <class T> inline T gt() {
register char c;
while (c = gc(), !isdigit(c) && c != '-') {}
register bool f = c == '-';
register T x = f ? 0 : c - '0';
for (c = gc(); isdigit(c); c = gc()) {
x = x * 10 + c - '0';
}
return f ? -x : x;
}
#endif
} // namespace io
namespace dinic {
const int MAXN = 100 + 5;
const int MAXM = 10000 + 5;
const int INF = INT_MAX;
struct Edge {
int v, f, nxt;
} E[MAXM << 1];
int N, M, S, T, flow, ffffc, tim;
int H[MAXN], cntE;
int Dis[MAXN], Cur[MAXN], Lv[MAXN];
int Flow[MAXM << 1];
bool bfs() {
static int Que[MAXN];
int qh = 0, qt = 0;
Dis[S] = 1, Lv[S] = ++tim;
for (Que[qt++] = S; qh < qt;) {
int &u = Que[qh++];
if (u == T) {
return true;
} else {
for (int i = H[u]; ~i; i = E[i].nxt) {
int &v = E[i].v;
if (E[i].f && Lv[v] != tim) {
Dis[v] = Dis[u] + 1;
Lv[v] = tim;
Que[qt++] = v;
}
}
}
}
return false;
}
void addEdge(int u, int v, int f = 1) {
E[++cntE] = (Edge) {v, f, H[u]};
H[u] = cntE;
E[++cntE] = (Edge) {u, 0, H[v]};
H[v] = cntE;
}
void init() {
M = io::gt<int>(), N = io::gt<int>();
S = 0, T = N + 1;
cntE = -1;
memset(H, -1, sizeof H);
while (true) {
int x = io::gt<int>(), y = io::gt<int>();
if (x + y == -2) {
break;
}
addEdge(x, y);
}
rep(i, 1, M + 1) {
addEdge(S, i);
}
rep(i, M + 1, N + 1) {
addEdge(i, T);
}
}
int dfs(int x, int maxf) {
if (x == T || !maxf) {
return maxf;
}
int left = maxf;
for (int &i = Cur[x]; ~i; i = E[i].nxt) {
int &v = E[i].v;
if (Dis[v] == Dis[x] + 1 && E[i].f && Lv[v] == tim) {
int flow = dfs(v, std::min(E[i].f, left));
if (!flow) {
Dis[v] = -1;
} else {
left -= flow;
E[i].f -= flow;
Flow[i ^ 1] += flow;
ffffc = 1;
if (!left) {
return maxf;
}
}
}
}
return maxf - left;
}
int dinic() {
int ans = 0;
while (true) {
while (bfs()) {
memcpy(Cur, H, sizeof(int) * (N + 1));
int res = dfs(S, INF);
ans += res;
}
if (!ffffc) {
break;
}
rep(i, 0, cntE + 1) {
E[i].f += Flow[i];
}
memset(Flow, 0, sizeof(int) * (cntE + 1));
ffffc = 0;
}
return ans;
}
void match() {
init();
int res = dinic();
if (res == 0) {
puts("No Solution!");
} else {
printf("%d\n", res);
rep(i, 1, M + 1) {
for (int j = H[i]; ~j; j = E[j].nxt) {
if(!E[j].f && E[j].v != S && E[j].v != T) {
printf("%d %d\n", i, E[j].v);
break;
}
}
}
}
}
} // namespace dinic
int main() {
dinic::match();
return 0;
}