OJ-Problems-Source/.ACM-Templates/Graph/图论-匹配.cpp
2016-08-13 23:35:41 +08:00

358 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//图论点、边集和二分图的相关概念和性质
//点覆盖: 点覆盖集即一个点集, 使得所有边至少有一个端点在集合里
//边覆盖: 边覆盖集即一个边集, 使得所有点都与集合里的边邻接
//独立集: 独立集即一个点集, 集合中任两个结点不相邻, 则称V为独立集
//团: 团即一个点集, 集合中任两个结点相邻
//边独立集: 边独立集即一个边集, 满足边集中的任两边不邻接
//支配集: 支配集即一个点集, 使得所有其他点至少有一个相邻点在集合里
//边支配集: 边支配集即一个边集, 使得所有边至少有一条邻接边在集合里
//最小路径覆盖: 用尽量少的不相交简单路径覆盖有向无环图G的所有顶点, 即每个顶点严格属于一条路径, 路径的长度可能为0(单个点)
//匹配: 匹配是一个边集, 满足边集中的边两两不邻接
//在匹配中的点称为匹配点或饱和点;反之, 称为未匹配点或未饱和点
//交错轨(alternating path): 图的一条简单路径, 满足任意相邻的两条边, 一条在匹配内, 一条不在匹配内
//增广轨(augmenting path): 是一个始点与终点都为未匹配点的交错轨
//最大匹配(maximum matching): 具有最多边的匹配
//匹配数(matching number): 最大匹配的大小
//完美匹配(perfect matching): 匹配了所有点的匹配
//完备匹配(complete matching): 匹配了二分图较小集合二分图X, Y中小的那个的所有点的匹配
//增广轨定理: 一个匹配是最大匹配当且仅当没有增广轨
//所有匹配算法都是基于增广轨定理: 一个匹配是最大匹配当且仅当没有增广轨
//最大匹配数 = 最小覆盖数 = 左边匹配点 + 右边未匹配点
//最小边覆盖 = 图中点的个数 - 最大匹配数 = 独立数
//二分图最大匹配
//Hungary + dfs
//邻接矩阵 O(V^2)
int uN, vN, match[N]; //左右点的数目
bool g[N][N], check[N];
bool dfs(int u) {
for (int v = 0; v < vN; v++) {
if (g[u][v] && !check[v]) {
check[v] = true;
if (match[v] == -1 || dfs(match[v])) {
match[v] = u; match[u] = v; return true;
}
}
}
return false;
}
//邻接表 O(V*E)
int head[N], to[M], nxt[M], tot, uN, match[N];
bool check[N];
void init() { tot = 0; memset(head, -1, sizeof(head)); }
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
bool dfs(int u) {
for (int i = head[u]; ~i; i = nxt[i]) {
int v = to[i];
if (!check[v]) {
check[v] = true;
if (match[v] == -1 || dfs(match[v])) {
match[v] = u; match[u] = v; return true;
}
}
}
return false;
}
int Hungary() {
memset(match, -1, sizeof(match));
int res = 0;
for (int u = 0; u < uN; u++) {
if (match[u] == -1) {
memset(check, 0, sizeof(check));
if (dfs(u)) { res++; }
}
}
return res;
}
//Hungary + bfs + 邻接矩阵 O(V*E)
bool g[N][N];
int uN, vN, match[N], check[N], pre[N];
int Hungary() {
memset(match, -1, sizeof(match));
memset(check, -1, sizeof(check));
queue<int> que;
int res = 0;
for (int i = 0; i < uN; i++) {
if (match[i] == -1) {
while (!que.empty()) { que.pop(); }
que.push(i); pre[i] = -1;
bool flag = false;
while (!que.empty() && !flag) {
int u = que.front(); que.pop();
for (int v = 0; v < vN && !flag; v++) {
if (g[u][v] && check[v] != i) {
check[v] = i; que.push(match[v]);
if (~match[v]) { pre[match[v]] = u; }
else {
flag = true;
for (int a = u, b = v; ~a;) {
int t = match[a]; match[a] = b; match[b] = a; a = pre[a]; b = t;
}
}
}
}
}
if (~match[i]) { res++; }
}
}
return res;
}
//Hungary + bfs + 邻接表 O(V*E)
int head[N], to[M], nxt[M], tot, uN, match[N], check[N], pre[N];
void init() { tot = 0; memset(head, -1, sizeof(head)); }
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
int Hungary() {
memset(match, -1, sizeof(match));
memset(check, -1, sizeof(check));
queue<int> que;
int res = 0;
for (int i = 0; i < uN; i++) {
if (match[i] == -1) {
while (!que.empty()) { que.pop(); }
que.push(i); pre[i] = -1;
bool flag = false;
while (!que.empty() && !flag) {
int u = que.front(); que.pop();
for (int i = head[u]; ~i && !flag; i = nxt[i]) {
int v = to[i];
if (check[v] != i) {
check[v] = i; que.push(match[v]);
if (~match[v]) { pre[match[v]] = u; }
else {
flag = true;
for (int a = u, b = v; ~a;) {
int t = match[a]; match[a] = b; match[b] = a; a = pre[a]; b = t;
}
}
}
}
}
if (~match[i]) { res++; }
}
}
return res;
}
//Hopcroft-Karp + vector + O(sqrt(V)*E)
const int INF = 0x3f3f3f3f;
vector<int> g[N];
int uN, matchx[N], matchy[N], dx[N], dy[N], dis;
bool check[N];
bool SearchP() {
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
queue<int> que; dis = INF;
for (int i = 0; i < uN; i++) { if (matchx[i] == -1) { dx[i] = 0; que.push(i); } }
while (!que.empty()) {
int u = que.front(); que.pop();
if (dx[u] > dis) { break; }
for (size_t i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (dy[v] == -1) {
dy[v] = dx[u] + 1;
if (matchy[v] == -1) { dis = dy[v]; }
else { dx[matchy[v]] = dy[v] + 1; que.push(matchy[v]); }
}
}
}
return dis != INF;
}
bool dfs(int u) {
for (size_t i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (!check[v] && dy[v] == dx[u] + 1) {
check[v] = true;
if (~matchy[v] && dy[v] == dis) { continue; }
if (matchy[v] == -1 || dfs(matchy[v])) {
matchy[v] = u; matchx[u] = v; return true;
}
}
}
return false;
}
int HK() {
memset(matchx, -1, sizeof(matchx));
memset(matchy, -1, sizeof(matchy));
int res = 0;
while (SearchP()) {
memset(check, 0, sizeof(check));
for (int i = 0; i < uN; i++) {
if (matchx[i] == -1 && dfs(i)) { res++; }
}
}
return res;
}
//二分图最佳匹配
//Kuhn-Munkers + 邻接矩阵 O(uN^2*vN)
//若求最小权匹配,可将权值取相反数,结果取相反数 点的编号从0开始
const int INF = 0x3f3f3f3f;
int uN, vN, g[N][N];
int matchy[N], lx[N], ly[N]; //y中各点匹配状态, 可行顶标
int slack[N]; //松弛数组
bool visx[N], visy[N];
bool dfs(int u) {
visx[u] = true;
for (int v = 0; v < vN; v++) {
if (visy[v]) { continue; }
int tmp = lx[u] + ly[v] - g[u][v];
if (tmp == 0) {
visy[v] = true;
if (matchy[v] == -1 || dfs(matchy[v])) { matchy[v] = u; return true; }
} else if (slack[v] > tmp) { slack[v] = tmp; }
}
return false;
}
int KM() {
memset(matchy, -1, sizeof(matchy));
memset(ly, 0, sizeof(ly));
for (int i = 0; i < uN; i++) { lx[i] = *max_element(g[i], g[i] + vN); }
for (int u = 0; u < uN; u++) {
memset(slack, 0x3f, sizeof(slack));
for (;;) {
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if (dfs(u)) { break; }
int d = INF;
for (int i = 0; i < vN; i++) { if (!visy[i] && d > slack[i]) { d = slack[i]; } }
for (int i = 0; i < uN; i++) { if (visx[i]) { lx[i] -= d; } }
for (int i = 0; i < vN; i++) { if (visy[i]) { ly[i] += d; } else { slack[i] -= d; } }
}
}
int res = 0;
for (int i = 0; i < vN; i++) { if (matchy[i] != -1) { res += g[matchy[i]][i]; } }
return res;
}
//二分图多重匹配
int uN, vN, g[N][N], match[N][N]; //match[i][0]为个数
int num[N]; //右边最大的匹配数
bool check[N];
bool dfs(int u) {
for (int v = 0; v < vN; v++) {
if (g[u][v] && !check[v]) {
check[v] = true;
if (match[v][0] < num[v]) { match[v][++match[v][0]] = u; return true; }
for (int i = 1; i <= num[0]; i++) {
if (dfs(match[v][i])) { match[v][i] = u; return true; }
}
}
}
return false;
}
int Hungary() {
int res = 0;
for (int i = 0; i < vN; i++) { match[i][0] = 0; }
for (int u = 0; u < uN; u++) {
memset(check, false, sizeof(check));
res += dfs(u);
}
return res;
}
//一般图最大匹配
//邻接矩阵 O(V^3)
int n, match[N];
bool g[N][N], check[N];
bool aug(int now) {
bool ret = false; check[now] = true;
for (int i = 0; i < n; i++) {
if (!check[i] && g[now][i]) {
if (match[i] == -1) { match[now] = i; match[i] = now; ret = true; }
else {
check[i] = true;
if (aug(match[i])) { match[now] = i, match[i] = now, ret = true; }
check[i] = false;
}
if (ret) { break; }
}
}
check[now] = false; return ret;
}
//邻接表 O(V*E)
int head[N], to[M], nxt[M], tot, match[N];
bool check[N];
void init() { tot = 0; memset(head, -1, sizeof(head)); }
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
bool aug(int now) {
bool ret = false; check[now] = true;
for (int i = head[now]; ~i; i = nxt[i]) {
int v = to[i];
if (!check[v]) {
if (match[v] == -1) { match[now] = v; match[v] = now; ret = true; }
else {
check[v] = true;
if (aug(match[v])) { match[now] = v; match[v] = now; ret = true; }
check[v] = false;
}
if (ret) { break; }
}
}
check[now] = false; return ret;
}
int graphMatch() {
memset(match, -1, sizeof(match));
memset(check, 0, sizeof(check));
for (int i = 0, j = n; i < n && j >= 2; i++) {
if (match[i] == -1 && aug(i)) { i = -1, j -= 2; }
}
int ret = 0;
for (int i = 0; i < n; i++) { ret += (match[i] != -1); }
return ret >> 1;
}
//带花树 + vector O(V^3) 点的编号从0开始
int n, match[N], fa[N], nxt[N], mark[N], vis[N], t;
vector<int> e[N]; queue<int> que;
int findfa(int n) { return n == fa[n] ? n : fa[n] = findfa(fa[n]); }
void unite(int x, int y) { fa[findfa(x)] = findfa(y); }
int lca(int x, int y) {
for (t++;; swap(x, y)) {
if (x == -1) { continue; }
if (vis[x = findfa(x)] == t) { return x; }
vis[x] = t; x = match[x] == -1 ? -1 : nxt[match[x]];
}
}
void group(int a, int p) {
for (int b, c; a != p; unite(a, b), unite(b, c), a = c) {
b = match[a]; c = nxt[b];
if (findfa(c) != p) { nxt[c] = b; }
if (mark[b] == 2) { mark[b] = 1; que.push(b); }
if (mark[c] == 2) { mark[c] = 1; que.push(c); }
}
}
void aug(int st) {
for (int i = 0; i < n; i++) { fa[i] = i; }
memset(nxt, -1, sizeof(nxt)); memset(vis, -1, sizeof(vis)); memset(mark, 0, sizeof(mark));
while (!que.empty()) { que.pop(); } que.push(st); mark[st] = 1;
while (match[st] == -1 && !que.empty()) {
int u = que.front(); que.pop();
for (int i = 0; i < (int)e[u].size(); i++) {
int v = e[u][i];
if (v == match[u] || findfa(u) == findfa(v) || mark[v] == 2) { continue; }
if (mark[v] == 1) {
int p = lca(u, v);
if (findfa(u) != p) { nxt[u] = v; }
if (findfa(v) != p) { nxt[v] = u; }
group(u, p); group(v, p);
} else if (match[v] == -1) {
nxt[v] = u;
for (int j = v, k, l; ~j; j = l) { k = nxt[j]; l = match[k]; match[j] = k; match[k] = j; }
break;
} else { nxt[v] = u; que.push(match[v]); mark[match[v]] = 1; mark[v] = 2; }
}
}
}
int solve(int n) {
memset(match, -1, sizeof(match)); t = 0; int ret = 0;
for (int i = 0; i < n; i++) { if (match[i] == -1) { aug(i); } }
for (int i = 0; i < n; i++) { ret += (match[i] > i); }
return ret;
}
//ural 1099
int main() {
int u, v;
scanf("%d", &n);
while (~scanf("%d%d", &u, &v)) { e[--u].push_back(--v); e[v].push_back(u); }
int ans = solve(n);
printf("%d\n", ans * 2);
for (int u = 0; u < n; u++) {
if (u < match[u]) { printf("%d %d\n", u + 1, match[u] + 1); }
}
}