Loading

「学习笔记」二分图相关定理与模型

最大独立集 \(=\) 二分图的点数 \(-\) 二分图的最大匹配数
最小路径覆盖 \(=\) 二分图的点数 \(-\) 二分图的最大匹配数
最小点覆盖 \(=\) 二分图的最大匹配数

例题

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\) 相邻。

posted @ 2023-04-02 19:25  yi_fan0305  阅读(21)  评论(0编辑  收藏  举报