题意
给定一张不一定合法的有源汇网络(包括每条边的容量与流量),你可以修改边的容量与流量,修改的代价是与原值的距离。问你最小修改代价,使得网络合法。
合法的定义是:每条边流量<=容量,除了源汇之外的点流量平衡
n
,
m
≤
100
n,m\leq 100
n,m≤100
分析
- 网络流题当然要用网络流解决。
- 对于 c ≥ f c\geq f c≥f的边,容易发现若最终 f ′ > c f'>c f′>c,则 c ′ = f ′ c'=f' c′=f′.否则 c ′ = c c'=c c′=c.
- 所以,连边 ( u , v , c − f , 1 ) , ( u , v , inf , 2 ) , ( v , u , f , 1 ) (u,v,c-f,1),(u,v,\inf,2),(v,u,f,1) (u,v,c−f,1),(u,v,inf,2),(v,u,f,1)
- 对于 c < f c<f c<f的边,若 c ≤ f ′ ≤ f c\leq f'\leq f c≤f′≤f,则花费固定为 f − c f-c f−c。
- 所以,先将f-c计入答案,然后连边 ( u , v , inf , 2 ) , ( v , u , f − c , 0 ) , ( v , u , c , 1 ) (u,v,\inf,2),(v,u,f-c,0),(v,u,c,1) (u,v,inf,2),(v,u,f−c,0),(v,u,c,1)
- 然后,计算每个点的流量盈亏并建超级源汇S,T。对于需要流出流量cnt的的点x,连边 ( S , x , c n t , 0 ) (S,x,cnt,0) (S,x,cnt,0),否则连边 ( x , T , − c n t , 0 ) (x,T,-cnt,0) (x,T,−cnt,0)
- 对此图跑最小费用最大流即可。由于有解,最终必定满流。
- 需要注意的是,由于原图源汇不需要流量平衡,所以可以看做存在一条 ( n , 1 ) (n,1) (n,1)的边,容量为inf,当前流量为0.特殊地,修改这条边的流量不需要代价。这样就可以把将有源汇转成循环流了。
#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = N * 20, inf = 1e8;
int n, m;
int final[N * 2], nex[M], tot = 1, to[M], f[M], cost[M];
int from[M], upd[M];
int ans, S, T;
void _link(int x, int y, int ff, int c) {
to[++tot] = y, nex[tot] = final[x], final[x] = tot;
f[tot] = ff, cost[tot] = c;
from[tot] = x;
}
void link(int x, int y, int ff, int c) {
_link(x, y, ff, c);
_link(y, x, 0, -c);
}
int Q[N * 4], cnt[N], vis[N];
int dis[N];
void spfa() {
int h = 0,t;
Q[t = 1] = S;
memset(dis,127,sizeof dis);
dis[S] = 0;
while (h < t) {
int x = Q[++h];
vis[x] = 0;
for(int i = final[x]; i; i = nex[i]) if(f[i]) {
int y = to[i];
if (dis[y] > dis[x] + cost[i]) {
dis[y] = dis[x] + cost[i];
upd[y] = i;
if (!vis[y]) {
vis[y] = 1;
Q[++t] = y;
}
}
}
}
}
int main() {
freopen("d.in","r",stdin);
cin >> n >> m;
for(int i = 1; i <= m; i++) {
int u, v, c, f;
scanf("%d %d %d %d",&u,&v,&c,&f);
cnt[u] -= f, cnt[v] += f;
if (c >= f) {
link(u, v, c - f, 1);
link(u, v, inf, 2);
link(v, u, f, 1);
} else {
ans += f - c;
link(v, u, c, 1);
link(v, u, f - c, 0);
link(u, v, inf, 2);
}
}
link(n, 1, inf, 0);
S = n + 1, T = S + 1;
for(int i = 1; i <= n; i++) {
if (cnt[i] > 0) {
link(S, i, cnt[i], 0);
} else if(cnt[i] < 0) {
link(i, T, -cnt[i], 0);
}
}
while (spfa(),dis[T] != dis[0]) {
int F = inf;
for(int z = upd[T]; z; z = upd[from[z]]) {
F = min(F, f[z]);
}
for(int z = upd[T]; z; z = upd[from[z]]) {
f[z] -= F, f[z ^ 1] += F;
ans += cost[z] * F;
}
}
cout << ans << endl;
}