约束每个点至少要经过一次,因此是上下界网络流。
每经过边都需要相应的边权,且要求耗费边权之和最小,因此是最小费用流。
存在多个终点,需要建立汇点 t t t ,因此是有源汇网络流。
即:有源汇上下界最小费用可行流。
建图
本题中结点 1 1 1 即可作为源点。
对于原图中存在的所有边,建下界为 1 1 1,上界为 i n f inf inf ,费用为对应边权的边。
由于存在多个终点,需要建立汇点 t 1 t1 t1,将除了 1 1 1 号点外的所有边向 t 1 t1 t1 连容量为 i n f inf inf 费用为 0 0 0 的边。
新建平衡源点 s 2 s2 s2 和平衡汇点 t 2 t2 t2,使原图中的点流量平衡。(具体原理和操作可见:网络流中有关上下界限制的学习笔记)
跑平衡源点 s 2 s2 s2 到平衡汇点 t 2 t2 t2 的最小费用最大流,即为可行流。注意由于每个边的下界为 1 1 1 ,因此最终答案还需要加上所有边的费用 ∗ 1 *1 ∗1。
参考代码
#include <bits/stdc++.h>
#define itn int
#define int long long
#define endl "\n"
#define PII pair<int, int>
using namespace std;
const int N = 2e5 + 10;
const int M = 5e4 + 10;
const itn inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
// const int mod = 998244353;
template <int N>
struct MCMF {
struct E {
int to, cap, val, inv;
};
vector<E> g[N];
int dis[N], now[N], h[N], pre[N], preu[N];
void add(int u, int v, int f, int w) {
g[u].push_back({v, f, w, (int)g[v].size()});
g[v].push_back({u, 0, -w, (int)g[u].size() - 1});
}
void dijkstra(int st) {
priority_queue<pair<int, int>, vector<pair<int, int>>,
greater<pair<int, int>>>
q;
memset(dis, 0x3f, sizeof dis);
memset(pre, -1, sizeof pre);
memset(preu, -1, sizeof preu);
dis[st] = 0;
q.push({0, st});
while (q.size()) {
// auto [d, u] = q.top();
auto qnow = q.top();
int d = qnow.first, u = qnow.second;
q.pop();
if (dis[u] < d)
continue;
int x = 0;
for (auto gnow : g[u]) {
int v = gnow.to, f = gnow.cap, w = gnow.val, inv = gnow.inv;
if (f && dis[v] > dis[u] + w + h[u] - h[v]) {
dis[v] = dis[u] + h[u] - h[v] + w;
pre[v] = x;
preu[v] = u;
q.push({dis[v], v});
}
x++;
}
}
}
pair<int, int> min_cost_max_flow(int st, int ed) {
memset(h, 0, sizeof h);
for (int flow = 0, cost = 0, res = inf;; res = inf) {
dijkstra(st);
if (dis[ed] > inf)
return {flow, cost};
for (int i = 0; i < N; i++) {
h[i] += dis[i];
}
for (int i = ed; i != st; i = preu[i]) {
res = min(res, g[preu[i]][pre[i]].cap);
}
flow += res;
cost += res * h[ed];
for (int i = ed; i != st; i = preu[i]) {
g[i][g[preu[i]][pre[i]].inv].cap += res;
g[preu[i]][pre[i]].cap -= res;
}
}
}
};
MCMF<505> mcmf;
int n, m, s1, t1, s2, t2;
int in[N];
void ins(int u, int v, int l, int r, int w) {
mcmf.add(u, v, r - l, w);
in[u] -= l, in[v] += l;
}
void solve() {
cin >> n;
s1 = n + 1, t1 = n + 2;
s2 = n + 3, t2 = n + 4;
int sum = 0;
for (int i = 1; i <= n; i++) {
int k;
cin >> k;
while (k--) {
int v, w;
cin >> v >> w;
ins(i, v, 1, inf, w);
sum += w;
}
}
for (int i = 2; i <= n; i++) {
mcmf.add(i, t1, inf, 0);
}
for (int i = 1; i <= t1; i++) {
if (in[i] < 0)
mcmf.add(i, t2, -in[i], 0);
else if (in[i] > 0)
mcmf.add(s2, i, in[i], 0);
}
mcmf.add(t1, 1, inf, 0);
cout << mcmf.min_cost_max_flow(s2, t2).second + sum;
}
signed main() {
ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cout << fixed << setprecision(12);
// init();
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}