二分图,匈牙利算法
dfs写法
const int maxn = 100024; struct nobe { int to; int lst; }edge[maxn]; int head[128]; int qsz = 1; inline void add(int u, int v) { edge[qsz].lst = head[u]; edge[qsz].to = v; head[u] = qsz++; } int match[128]; bool check[128]; bool dfs(int u) { int i, v; for (i=head[u]; i; i=edge[i].lst) { v = edge[i].to; if (!check[v]) { check[v] = true; if (match[v]==-1 || dfs(match[v])) { match[v] = u; match[u] = v; return true; } } } return false; } // 返回匹配个数 匹配结果存储在matching中 int GetAns(int n) { int ans = 0; memset(match, -1, sizeof(match)); for (int i=1; i<=n; ++i) { // i是从一个匹配集合开始for的,(左边集合 或者 右边集合.) if (match[i] == -1) { memset(check, 0, sizeof(check)); ans += dfs(i); } } return ans; }
bfs写法
int match[128]; // 匹配的边 int vis[128]; // 注意是int类型 int prevv[128]; // int bfs(int m) { queue<int> Q; int i, j, u, v, ans = 0; memset(match, -1, sizeof(match)); memset(vis, -1, sizeof(vis)); for (i=1; i<=m; ++i) { if (match[i] == -1) { while (!Q.empty()) Q.pop(); Q.push(i); prevv[i] = -1; bool flag = false; while (!Q.empty() && !flag) { u = Q.front(); for (j=head[u]; j && !flag; j=edge[j].lst) { v = edge[j].to; if (vis[v] != i) { vis[v] = i; Q.push(match[v]); if (match[v] >= 0) { prevv[match[v]] = u; } else { flag = true; int d = u, e = v; while (d != -1) { int t = match[d]; match[d] = e; match[e] = d; d = prevv[d]; e = t; } } } } Q.pop(); } if (match[i] != -1) ans++; } } return ans; }
同时bfs优于dfs,但是dfs好理解.
附上学习的博客 : http://www.renfei.org/blog/bipartite-matching.html
然后附上一些性质
补充定义和定理:
最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:最大匹配数 = 最大独立数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数
百科说: 最大匹配数=最大流=最小割=最小点集覆盖
有向无环图的最大匹配数.
https://vjudge.net/problem/HDU-1151
const int maxn = 100024; struct nobe { int to; int lst; }edge[maxn]; int head[128]; int qsz = 1; inline void add(int u, int v) { edge[qsz].lst = head[u]; edge[qsz].to = v; head[u] = qsz++; } int match[128]; int vis[128]; bool dfs(int u) { int i, v; for (i=head[u]; i; i=edge[i].lst) { v = edge[i].to; if (vis[v]) continue; vis[v] = true; if (match[v]==-1 || dfs(match[v])) { match[v] = u; return true; } } return false; } int main() { int t, n, m, i; scanf("%d", &t); while (t--) { memset(head, 0, sizeof(head)); scanf("%d%d", &n, &m); int u, v; while (m--) { scanf("%d%d", &u, &v); add(u, v); } int res = 0; memset(match, -1, sizeof(match)); for (i=1; i<=n; ++i) { memset(vis, 0, sizeof(vis)); res += dfs(i); } printf("%d\n", n - res); } return 0; }
无向图的最大匹配数.
https://vjudge.net/problem/HihoCoder-1122
int match[10024]; bool vis[10024]; bool dfs(int u) { int i, v; for (i=head[u]; i; i=edge[i].lst) { v = edge[i].to; if (!vis[v]) { vis[v] = true; if (match[v]==-1 || dfs(match[v])) { match[v] = u; match[u] = v; return true; } } } return false; } int main() { int u, v, i, n, m; scanf("%d%d", &n, &m); while (m--) { scanf("%d%d", &u, &v); add(u, v); add(v, u); // 双向边. 听说有些情况是要答案除以2的 } int res = 0; memset(match, -1, sizeof(int) * (n + 10)); for (i=1; i<=n; i++) { memset(vis, 0, sizeof(int) * (n + 10)); if (match[i] == -1) res += dfs(i); } printf("%d\n", res); return 0; }