• @codeforces


    @description@

    给定一个有源点与汇点的图 G,并对于每一条边 (u, v) 给定 f(u, v) 与 c(u, v)。f 表示流量,c 表示容量。G 不一定是合法的网络流。

    求一个新图 G',使得 G' 是一个合法网络流(流量守恒与容量限制) ,且 ∑(|f'(u, v) - f(u, v)| + |c'(u, v) - c(u, v)|) 最小。

    输出这个最小值。

    input
    第一行两个整数 n 和 m (2 ≤ n ≤ 100, 0 ≤ m ≤ 100),表示点数与边数。
    接下来 m 行每行 4 个整数 ui, vi, ci, fi (1 ≤ ui, vi ≤ n, ui ≠ vi, 0 ≤ ci, fi ≤ 1 000 000) ,表示图 G 中有一条 (ui, vi) 且容量为 ci,流量为 fi 的图。
    保证 1 为源点而 n 为汇点。输入无自环,但可能有重边。

    output
    输出一个整数,表示最小值。

    sample input1
    2 1
    1 2 2 1
    sample output1
    0
    note
    注意不一定是最大流,比如上面的样例。

    sample input2
    4 2
    2 3 1 1
    3 2 1 1
    sample output2
    0
    note
    可能会有独立于源点和汇点的流存在。

    @solution@

    新的容量 c'(u, v) = max(c(u, v), f'(u, v))。
    贪心一下就可以得到这个结果。

    因此,我们分类讨论:

    (1)c(u, v) >= f(u, v)。
    当我们将 f(u, v) - 1,则费用 + 1。
    因为一条边流量减少等于其反向边流量增加,我们连 (v, u),容量为 f(u, v),费用为 1。
    当我们将 f(u, v) + 1,如果新的流量不超过 c(u, v),则费用 + 1;否则,费用 + 2。
    因为新的流量越大单位费用不会减少,所以我们连两类边:一是 (u, v),容量为 c(u, v) - f(u, v),费用为 1;二是 (u, v),容量为 inf,费用为 2。
    每次沿最短路增广,所以必然先走一类边再走二类边。

    (2)c(u, v) < f(u, v)
    与上面不同的是,这个时候必然会产生费用 f(u, v) - c(u, v)。我们可以先把这部分费用算入答案。
    当 f(u, v) + 1,费用 + 2。连 (u, v),容量为 inf,费用为 2。
    当 f(u, v) - 1,如果新的流量 >= c(u, v),则不会产生费用:因为这部分费用在一开始就被算入了答案;否则费用 + 1。
    因此,我们依然是连两类边:一是 (v, u),容量为 f(u, v) - c(u, v),费用为 0;二是 (v, u),容量为 c(u, v),费用为 1。

    为了使流量守恒,我们统计一个点的进入的流量与出去的流量之差。
    如果进入的流量 > 出去的流量,则多出来的流向一个超级汇点流去,即建边 (i, tt),容量为差值,费用为 0。
    如果进入的流量 < 出去的流量,则差的流由一个超级源点流过来,即建边 (ss, i),容量为差值,费用为 0。

    原图中源点 s 和汇点 t 不一定满足流量守恒怎么办?连边 (t, s),容量为 inf,费用为 0。

    这样子,ss 向 tt 跑最小费用最大流即可。

    @accepted code@

    #include<queue>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXV = 400 + 5;
    const int MAXE = 20000 + 5;
    const int INF = int(1E9);
    struct FlowGraph{
    	struct edge{
    		int to, flow, cap, dis;
    		edge *nxt, *rev;
    	}edges[MAXE], *adj[MAXV], *cur[MAXV], *ecnt;
    	int s, t, cost, dist[MAXV];
    	void init() {
    		ecnt = &edges[0];
    		for(int i=0;i<MAXV;i++) adj[i] = NULL;
    	}
    	void addedge(int u, int v, int c, int w) {
    		edge *p = (++ecnt), *q = (++ecnt);
    		p->to = v, p->flow = 0, p->cap = c, p->dis = w;
    		p->nxt = adj[u], adj[u] = p;
    		q->to = u, q->flow = 0, q->cap = 0, q->dis = -w;
    		q->nxt = adj[v], adj[v] = q;
    		p->rev = q, q->rev = p;
    	}
    	bool inque[MAXV];
    	bool relabel() {
    		queue<int>que;
    		for(int i=0;i<MAXV;i++) dist[i] = INF, cur[i] = adj[i];
    		que.push(s); dist[s] = 0, inque[s] = true;
    		while( !que.empty() ) {
    			int f = que.front(); que.pop(); inque[f] = false;
    			for(edge *p=adj[f];p;p=p->nxt) {
    				if( p->cap > p->flow && dist[f] + p->dis < dist[p->to] ) {
    					dist[p->to] = dist[f] + p->dis;
    					if( !inque[p->to] ) {
    						que.push(p->to);
    						inque[p->to] = true;
    					}
    				}
    			}
    		}
    		return !(dist[t] == INF);
    	}
    	bool vis[MAXV];
    	int aug(int x, int tot) {
    		if( x == t ) {
    			cost += tot*dist[x];
    			return tot;
    		}
    		int sum = 0; vis[x] = true;
    		for(edge *&p=cur[x];p;p=p->nxt) {
    			if( p->cap > p->flow && !vis[p->to] && dist[p->to] == dist[x] + p->dis ) {
    				int del = aug(p->to, min(tot-sum, p->cap-p->flow));
    				p->flow += del, p->rev->flow -= del, sum += del;
    				if( sum == tot ) break;
    			}
    		}
    		vis[x] = false;
    		return sum;
    	}
    	int min_cost_max_flow(int _s, int _t) {
    		s = _s, t = _t; int flow = 0; cost = 0;
    		while( relabel() )
    			flow += aug(s, INF);
    		return flow;
    	}
    }G;
    int deg[MAXV];
    int main() {
    	G.init(); int n, m;
    	scanf("%d%d", &n, &m);
    	int s = 1, t = n, ss = 0, tt = n + 1, ans = 0;
    	for(int i=1;i<=m;i++) {
    		int u, v, c, f; scanf("%d%d%d%d", &u, &v, &c, &f);
    		if( c < f ) {
    			G.addedge(v, u, c, 1);
    			G.addedge(u, v, f - c, 0);
    			G.addedge(u, v, INF, 2);
    			ans += f - c;
    			deg[u] += c, deg[v] -= c;
    		}
    		else {
    			G.addedge(v, u, f, 1);
    			G.addedge(u, v, c - f, 1);
    			G.addedge(u, v, INF, 2);
    			deg[u] += f, deg[v] -= f;
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		if( deg[i] < 0 ) G.addedge(ss, i, -deg[i], 0);
    		if( deg[i] > 0 ) G.addedge(i, tt, deg[i], 0);
    	}
    	G.addedge(t, s, INF, 0);
    	G.min_cost_max_flow(ss, tt);
    	printf("%d
    ", ans + G.cost);
    }
    

    @details@

    除此之外,还有另一种建模方法:

    考虑一个零流的图,它本身会产生一定的费用。
    我们给某一条边的流量增加,费用可能会减少(更接近原图 G 中的流量),不变或者是增加。
    我们建一个图,使得这个图可以满足让流量增加过后费用的变化规律。然后在这个图上找一个最小费用循环流。

  • 相关阅读:
    IP地址分类整理
    PHP
    [转载]数组的全排列问题
    使用 Homebrew 安装 Git
    Homebrew简介及安装
    iOS开发~CocoaPods使用详细说明
    关于目前自己iOS项目使用的第三方开源库
    IOS 时间格式 时间转换 大总结
    Xcode磁盘空间大清理
    Swift百万线程攻破单例(Singleton)模式
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10384431.html
Copyright © 2020-2023  润新知