若是一张无向图的 \(N\) 个节点( \(N \geq 2\) ),能够分红 \(U\) , \(V\) 两个非空集合,其中 \(U \cap V = \Phi\) ,而且在同一集合内的点之间都没有边相连,那么称这张无向图为一张二分图。ios
\(U\) , \(V\) 分别为二分图的左部和右部。算法
顶点集 \(U\) , \(V\) 被称为是图的两个部分。函数
等价的 , 二分图 能够被定义成 图中全部的环都有偶数个顶点。ui
一张无向图是二分图,当且仅当图中不存在奇环。spa
若是某个图是二分图,那么它至少有两个结点,且全部回路的长度均为偶数,任何无回路的图均是二分图。code
一旦添加一条边后图中出现了回路,且长度必定为奇数,则该图就再也不是二分图。对象
根据上述定理,能够用染色法进行二分图断定。blog
用黑白两种颜色标记图中的节点,当一个节点被标记后,它的全部相邻节点应该被标记成与它相反的颜色。每一个点只标记一次。图片
若标记过程当中产生冲突,则说明存在奇环。get
二分图染色通常基于 \(DFS\) 。
时间复杂度为 \(O ( N + M )\) 。
/* By 《算法竞赛进阶指南》 */ void dfs(int x, int col) { 赋值 v[x] ← col; 对于与 x 相连的每条无向边(x, y) if (v[y] = 0) dfs(y, 3 - col) else if (v[y] = col) { 不是二分图; return; } } 主函数中 { for (i = 1 → N) if (v[i] = 0) dfs(i, 1); 断定无向图是二分图; }
云:
“任意两条边都没有公共端点”的边的集合被称为图的一组。
学长云:
给定一张图 \(G\) , 在 \(G\) 的一子图 \(M\) 中 , \(M\) 的边集中的任意两条边都没有共同的端点 , 则称 \(M\) 是一个匹配。
by @Lucky Block
上图中的选择方案即为原图的一种匹配。
云:
在二分图中,包含边数最多的一组匹配被称为二分图的最大匹配。
学长又云:
给定一张图 \(G\) , 其中边数最多的匹配 , 即该图的最大匹配。
对于一匹配 \(M\) ,增广路径是指从 \(M\) 中未使用的顶点开始 , 并从 \(M\) 中未使用的顶点结束的交替路径 。
能够证实 , 一个匹配是最大匹配 , 当且仅当它没有任何增广路经。
即寻找增广路径 , 它是一种用 增广路径 求 二分图最大匹配的算法。
设 \(S=\Phi\) ,即全部边都是非匹配边。
寻找增广路 \(path\) ,把路径上全部边的匹配状态取反,获得一个更大的匹配 \(S'\) 。
重复第 \(2\) 步,知道图中不存在增广路。
对 \(Yugari\) 进行匹配 :
其直接链接点 \(Reimu\) 未被匹配 , 则将 \(Yugari\) 与 \(Reimu\) 进行匹配
对 \(Marisa\) 进行匹配 :
其直接链接点 \(Patchouli\) 未被匹配 , 则将 \(Marisa\) 与 \(Patchouli\) 进行匹配
对 \(Suika\) 进行匹配 :
其直接链接点 \(Reimu\) 被匹配 , 检查 \(Reimu\) 的匹配点 \(Yugari\) 可否寻找到其余匹配点
\(Yugari\) 可与 \(Yuyuko\) 进行匹配 , 则将 \(Yugari\) 与 \(Yuyuko\) 进行匹配
因为\(Yugari\) 匹配对象改变 , \(Reimu\) 未被匹配 , 则将 \(Suika\) 与 \(Reimu\) 进行匹配
对 \(Aya\) 进行匹配 :
其直接链接点 \(Reimu\) 被匹配 , 检查 \(Reimu\) 的匹配点 \(Suika\) 可否寻找到其余匹配点
\(Suika\) 无其余匹配点 , 不可将 \(Suika\) 与其余结点进行匹配
因为 \(Suika\) 匹配对象不可改变 , \(Reimu\) 被匹配 , 则 \(Aya\) 无匹配点
则此二分图的一种最大匹配为 :
[P3386 【模板】二分图匹配](P3386 【模板】二分图匹配)
/* Name: P3386 【模板】二分图匹配 Solution: 二分图匹配 By Frather_ */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; /*=========================================快读*/ int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); } return x * f; } /*=====================================定义变量*/ int n, m, t; const int _ = 10010; struct edge { int from; int to; int nxt; } e[_]; int cnt, head[_]; bool vis[_]; int mc[_]; int ans; /*===================================自定义函数*/ void add(int from, int to) { e[++cnt].from = from; e[cnt].to = to; e[cnt].nxt = head[from]; head[from] = cnt; } bool check(int u_) { for (int i = head[u_]; i; i = e[i].nxt) if (!vis[e[i].to]) { vis[e[i].to] = true; if (!mc[e[i].to] || check(mc[e[i].to])) { mc[e[i].to] = u_; return true; } } return false; } /*=======================================主函数*/ int main() { n = read(); m = read(); t = read(); for (int i = 1; i <= t; i++) { int u = read(); int v = read(); add(u, v); } for (int i = 1; i <= n; i++) { memset(vis, false, sizeof(vis)); if (check(i)) ans++; } printf("%d\n", ans); return 0; }
/* Name: P2756 飞行员配对方案问题 Solution: 二分图匹配 By Frather_ */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; /*=========================================快读*/ int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); } return x * f; } /*=====================================定义变量*/ int n, m, t; const int _ = 10010; struct edge { int from; int to; int nxt; } e[_]; int cnt, head[_]; bool vis[_]; int mc[_]; int ans; /*===================================自定义函数*/ void add(int from, int to) { e[++cnt].from = from; e[cnt].to = to; e[cnt].nxt = head[from]; head[from] = cnt; } bool check(int u_) { for (int i = head[u_]; i; i = e[i].nxt) if (!vis[e[i].to]) { vis[e[i].to] = true; if (!mc[e[i].to] || check(mc[e[i].to])) { mc[e[i].to] = u_; return true; } } return false; } /*=======================================主函数*/ int main() { n = read(); m = read(); while (1) { int u = read(); int v = read(); if (u == -1 || v == -1) break; add(u, v); } for (int i = 1; i <= n; i++) { memset(vis, false, sizeof(vis)); if (check(i)) ans++; } printf("%d\n", ans); for (int i = n + 1; i <= m; i++) if (mc[i]) printf("%d %d\n", mc[i], i); return 0; }
本文图片均来自 @Lucky Block。
----至已逝去的不是学长大大(((不是
鸣谢:
《算法竞赛进阶指南》
@Lucky Block
OI-wiki
百度百科
Luogu