边覆盖相关问题
边覆盖指选则一些边使得所有顶点都被覆盖到。
最小边覆盖
考虑一个无权无向图的最小边覆盖问题如何解决。
我们用贪心不难发现:不可能选择一条路径超过 \(3\) 长度,否则去掉中间的边照样是边覆盖。
所以最终答案会变成每个连通块都是中间一个点,然后剩下的点全部连向中间这个点。
不妨设总点数为 \(n\),有 \(t\) 个连通块,则我们会选 \(n - t\) 条边。
显然我们希望 \(t\) 尽可能大,发现一个边覆盖会对应到 \(t\) 大小的最大匹配上,所以最终结论就是 \(n\) 减去最大匹配。注意这个不要求必须是二分图。
带权版本
现在考虑二分图
显然上题中形状如鸡爪依然成立。
我们考虑每个点邻边最小边 \(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;
}