「学习笔记」二分图相关定理与模型
最大独立集 \(=\) 二分图的点数 \(-\) 二分图的最大匹配数
最小路径覆盖 \(=\) 二分图的点数 \(-\) 二分图的最大匹配数
最小点覆盖 \(=\) 二分图的最大匹配数
例题
POJ 3692
最大独立集的模型
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;
#define id(x) (x + g)
const int N = 510;
int g, b, m, T, cnt = 1, S, K;
ll maxflow;
int h[N], dep[N], cur[N];
bool inque[N], ok[N][N];
struct edge {
int v, nxt;
ll w;
} e[(N * N) << 1];
void add(int u, int v, ll w) {
e[++ cnt].nxt = h[u];
e[h[u] = cnt].v = v;
e[cnt].w = w;
}
bool bfs() {
queue<int> q;
for (int i = S; i <= T; ++ i) {
dep[i] = 1e9;
cur[i] = h[i];
inque[i] = 0;
}
q.push(S);
dep[S] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
inque[u] = 0;
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (dep[v] > dep[u] + 1 && e[i].w) {
dep[v] = dep[u] + 1;
if (!inque[v]) {
q.push(v);
inque[v] = 1;
}
if (v == T) return true;
}
}
}
return false;
}
ll dfs(int u, ll flow) {
if (u == T) {
maxflow += flow;
return flow;
}
ll rlow = 0, used = 0;
for (int &i = cur[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (e[i].w && dep[v] == dep[u] + 1) {
if ((rlow = dfs(v, min(flow - used, e[i].w)))) {
used += rlow;
e[i].w -= rlow;
e[i ^ 1].w += rlow;
if (used == flow) break;
}
}
}
return used;
}
void work() {
++ K;
memset(ok, 0, sizeof ok);
memset(e, 0, sizeof e);
memset(h, 0, sizeof h);
maxflow = 0;
cnt = 1;
T = g + b + 1;
for (int i = 1, a, b; i <= m; ++ i) {
scanf("%d%d", &a, &b);
ok[a][b] = 1;
}
for (int i = 1; i <= g; ++ i) {
add(0, i, 1);
add(i, 0, 0);
for (int j = 1; j <= b; ++ j) {
if (ok[i][j]) continue;
add(i, id(j), 1);
add(id(j), i, 0);
}
}
for (int i = 1; i <= b; ++ i) {
add(id(i), T, 1);
add(T, id(i), 0);
}
while (bfs()) {
dfs(S, 1e9);
}
printf("Case %d: %lld\n", K, g + b - maxflow);
}
int main() {
scanf("%d%d%d", &g, &b, &m);
while (g || b || m) {
work();
scanf("%d%d%d", &g, &b, &m);
}
return 0;
}
POJ 3020
最小路径覆盖的模型
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
#define r(i) ((i) + cot)
const int N = 510;
const int dx[4] = {0, 1, 0, -1};
const int dy[4] = {1, 0, -1, 0};
int t, n, m, cnt, cot, S, T;
ll maxflow;
int id[N][N], h[N << 1], cur[N << 1], dep[N << 1];
bool inque[N << 1];
struct edge {
int v, nxt;
ll w;
} e[(N * N) << 1];
void clear() {
cnt = 1;
cot = 0;
maxflow = 0;
memset(h, 0, sizeof h);
memset(id, 0, sizeof id);
}
void add(int u, int v, ll w) {
e[++ cnt].nxt = h[u];
e[h[u] = cnt].v = v;
e[cnt].w = w;
}
bool bfs() {
queue<int> q;
for (int i = S; i <= T; ++ i) {
dep[i] = 1e9;
cur[i] = h[i];
inque[i] = 0;
}
q.push(S);
dep[S] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
inque[u] = 0;
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (e[i].w && dep[v] > dep[u] + 1) {
dep[v] = dep[u] + 1;
if (!inque[v]) {
q.push(v);
inque[v] = 1;
}
if (v == T) return true;
}
}
}
return false;
}
ll dfs(int u, ll flow) {
if (u == T) {
maxflow += flow;
return flow;
}
ll used = 0, rlow;
for (int &i = cur[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (dep[v] == dep[u] + 1 && e[i].w) {
if (rlow = dfs(v, min(e[i].w, flow - used))) {
e[i].w -= rlow;
e[i ^ 1].w += rlow;
used += flow;
if (used == flow) break;
}
}
}
return used;
}
void work() {
clear();
scanf("%d%d\n", &n, &m);
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
char x;
scanf("%c", &x);
if (x == '*') {
id[i][j] = ++ cot;
}
}
getchar();
}
T = cot + cot + 1;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
if (id[i][j]) {
add(S, id[i][j], 1);
add(id[i][j], S, 0);
add(r(id[i][j]), T, 1);
add(T, r(id[i][j]), 0);
for (int k = 0; k < 4; ++ k) {
if (id[i + dx[k]][j + dy[k]]) {
add(id[i][j], r(id[i + dx[k]][j + dy[k]]), 1);
add(r(id[i + dx[k]][j + dy[k]]), id[i][j], 0);
}
}
}
}
}
while (bfs()) {
dfs(S, 1e18);
}
printf("%lld\n", cot - maxflow / 2);
}
int main() {
scanf("%d", &t);
while (t --) {
work();
}
return 0;
}
称一张图 \(G\) 是二分图当且仅当点集 \(V\) 可以分为两半 \(A, B\) ,使得所有边都可以写成 \(<a, b> \in E, a \in A, b \in B\)。
若存在一种办法使得所有点集 \(A, B\) 的点两两匹配,则称二分图 \(G\) 存在完美匹配。
显然,二分图 \(G\) 存在完美匹配的必要条件是 \(|A| = |B|\)。
一个二分图 \(G\) 存在完美匹配,当且仅当 \(A\) 的任意子集 \(S\), \(B\) 中至少有 \(|S|\) 个不同的点与 \(S\) 相邻。
朝气蓬勃 后生可畏