边覆盖相关问题

边覆盖指选则一些边使得所有顶点都被覆盖到。

最小边覆盖

考虑一个无权无向图的最小边覆盖问题如何解决。

我们用贪心不难发现:不可能选择一条路径超过 \(3\) 长度,否则去掉中间的边照样是边覆盖。

所以最终答案会变成每个连通块都是中间一个点,然后剩下的点全部连向中间这个点。

不妨设总点数为 \(n\),有 \(t\) 个连通块,则我们会选 \(n - t\) 条边。

显然我们希望 \(t\) 尽可能大,发现一个边覆盖会对应到 \(t\) 大小的最大匹配上,所以最终结论就是 \(n\) 减去最大匹配。注意这个不要求必须是二分图。

带权版本

例题 [ABC231H] Minimum Coloring

现在考虑二分图

显然上题中形状如鸡爪依然成立。

我们考虑每个点邻边最小边 \(a(u)\),对于一个方案 \(T\),其中心点所选匹配边集合为 \(R\),则其答案为:

\[\sum_{e \in R}w(e) + \sum_{u \notin R}a(u) \]

变一下形我们可以得到:

\[\sum_{u}a(u) - \sum_{u \in R}w(u) + \sum_{e \in R}w(e) \]

不妨将一条边的边权改写为 \(w'(e) = w(e) - a(u) - a(v)\)。则我们可以得到:

\[\sum_{u}a(u) + \sum_{e \in R}w'(e) \]

于是这就转换成了最小权匹配问题,可以解决。

点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2005;
const long long inf = 0x3f3f3f3f3f3f3f3fll;

struct Edge {
	int to, val;
	long long cst;
	int rev;
	Edge (int _to = 0, int _val = 0, int _cst = 0, int _rev = 0) :
		to(_to), val(_val), cst(_cst), rev(_rev) {} 
};
vector<Edge> e[N];
void add(int u, int v, int w, int c) {
	e[u].push_back(Edge(v, w, c, (int)e[v].size()));
	e[v].push_back(Edge(u, 0, -c, (int)e[u].size() - 1));
}

long long d[N] = {0};
bool inq[N] = {false};

bool bfs(int s, int t) {
	memset(d, 0x3f, sizeof d);
	memset(inq, false, sizeof inq);
	queue<int> q;
	q.push(s), d[s] = 0ll;
	while (!q.empty()) {
		int h = q.front();
		q.pop();
		inq[h] = false;
		for (auto i: e[h])
			if (i.val > 0 && d[i.to] > d[h] + i.cst) {
				d[i.to] = d[h] + i.cst;
				if (!inq[i.to])
					inq[i.to] = true, q.push(i.to);
			}
	}
	return d[t] != inf;
}

int cur[N] = {0};
bool vis[N] = {false};

int dfs(int x, int t, int f) {
	if (x == t)
		return f;
	int fl = 0;
	vis[x] = true;
	for (int i = cur[x]; i < (int)e[x].size(); i = ++cur[x]) 
		if (!vis[e[x][i].to] && d[e[x][i].to] == d[x] + e[x][i].cst && e[x][i].val > 0) {
			fl = dfs(e[x][i].to, t, min(f, e[x][i].val));
			if (fl > 0) {
				e[x][i].val -= fl;
				e[e[x][i].to][e[x][i].rev].val += fl;
				break;
			}
		}
	vis[x] = false;
	if (fl == 0)
		d[x] = -1;
	return fl;
}

long long Dinic(int s, int t) {
	long long sum = 0ll;
	while (bfs(s, t) && d[t] < 0) {
		memset(cur, 0, sizeof cur);
		int ad = 0;
		while ((ad = dfs(s, t, 0x3f3f3f3f)) > 0)
			sum += d[t] * ad;
	} 
	return sum;
}

int n, m, k;
int u[N] = {0}, v[N] = {0}, w[N] = {0};
int a[N] = {0}; 

int main() {
	cin >> n >> m >> k;
	memset(a, 0x3f, sizeof a);
	for (int i = 1; i <= k; i++) {
		cin >> u[i] >> v[i] >> w[i];
		a[u[i]] = min(a[u[i]], w[i]);
		a[v[i] + n] = min(a[v[i] + n], w[i]);
	}
	int S = 0, T = n + m + 1;
	for (int i = 1; i <= n; i++)
		add(S, i, 1, 0);
	for (int i = 1; i <= m; i++)
		add(i + n, T, 1, 0);
	for (int i = 1; i <= k; i++) 
		add(u[i], v[i] + n, 1, w[i] - a[u[i]] - a[v[i] + n]);
	long long ans = 0ll;
	for (int i = 1; i <= n + m; i++)
		ans += a[i];
	cout << ans + Dinic(S, T) << endl;
	return 0;
} 
posted @ 2024-04-16 21:33  rlc202204  阅读(11)  评论(0编辑  收藏  举报