cf708D Incorrect Flow (网络流)

题意

给定一张不一定合法的有源汇网络(包括每条边的容量与流量),你可以修改边的容量与流量,修改的代价是与原值的距离。问你最小修改代价,使得网络合法。
合法的定义是:每条边流量<=容量,除了源汇之外的点流量平衡
n , m ≤ 100 n,m\leq 100 n,m100

分析

  • 网络流题当然要用网络流解决。
  • 对于 c ≥ f c\geq f cf的边,容易发现若最终 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,cf,1),(u,v,inf,2),(v,u,f,1)
  • 对于 c < f c<f c<f的边,若 c ≤ f ′ ≤ f c\leq f'\leq f cff,则花费固定为 f − c f-c fc
  • 所以,先将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,fc,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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>